Repository: ncbi/icn3d
Branch: master
Commit: 4d0ce46f2908
Files: 350
Total size: 32.3 MB
Directory structure:
gitextract_1bkdhxzz/
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── SECURITY.md
├── build/
│ ├── icn3d.js
│ └── icn3d.module.js
├── css/
│ └── icn3d.css
├── dist/
│ ├── CHANGELOG.md
│ ├── CODE_OF_CONDUCT.md
│ ├── LICENSE.md
│ ├── README.md
│ ├── example/
│ │ ├── addAnnoLocal.html
│ │ ├── annoLocal.js
│ │ ├── example.html
│ │ ├── loadStateFile.js
│ │ └── module.html
│ ├── full.html
│ ├── full2.html
│ ├── full2_3.49.0.html
│ ├── full_3.49.0.html
│ ├── icn3d.css
│ ├── icn3d.html
│ ├── icn3d.js
│ ├── icn3d.module.js
│ ├── icn3d_3.49.0.css
│ ├── index.html
│ ├── mtz.wasm
│ ├── notfound.html
│ ├── script/
│ │ ├── d3v4-force-all.js
│ │ ├── jszip.js
│ │ ├── mtz.js
│ │ ├── mtz.wasm
│ │ └── pako.js
│ ├── share.html
│ └── share2.html
├── example/
│ ├── addAnnoLocal.html
│ ├── annoLocal.js
│ ├── collection/
│ │ ├── 1wfa.pdb
│ │ └── json_only.json
│ ├── example-simple.html
│ ├── example.html
│ ├── loadStateFile.js
│ └── module.html
├── full.html
├── full2.html
├── gulpfile.js
├── icn3d.html
├── icn3dnode/
│ ├── README.md
│ ├── addmissingatoms.js
│ ├── annotation.js
│ ├── cdsearch.js
│ ├── delphipot.js
│ ├── epitope.js
│ ├── epitope_neighbor.js
│ ├── general_id_cmd.js
│ ├── interaction.js
│ ├── interaction2.js
│ ├── interactiondetail.js
│ ├── interactiondetail2.js
│ ├── ligand.js
│ ├── refnum.js
│ ├── refnum_file.js
│ ├── refpdb/
│ │ ├── 1B2Microglobulin_7phrL_human_C1.pdb
│ │ ├── 1BTLA_2aw2A_human_Iset.pdb
│ │ ├── 1CD19_6al5A_human-n1.pdb
│ │ ├── 1CD28_1yjdC_human_V.pdb
│ │ ├── 1CD2_1hnfA_human_C2-n2.pdb
│ │ ├── 1CD2_1hnfA_human_V-n1.pdb
│ │ ├── 1CD3d_6jxrd_human_C1.pdb
│ │ ├── 1CD3e_6jxrf_human_C1.pdb
│ │ ├── 1CD3g_6jxrg_human_C2.pdb
│ │ ├── 1CD8a_1cd8A_human_V.pdb
│ │ ├── 1Contactin1_2ee2A_human_FN3-n9.pdb
│ │ ├── 1Contactin1_3s97C_human_Iset-n2.pdb
│ │ ├── 1ECadherin_4zt1A_human_n2.pdb
│ │ ├── 1FAB-HEAVY_5esv_C1-n2.pdb
│ │ ├── 1FAB-HEAVY_5esv_V-n1.pdb
│ │ ├── 1FAB-LIGHT_5esv_C1-n2.pdb
│ │ ├── 1FAB-LIGHT_5esv_V-n1.pdb
│ │ ├── 1GHR_1axiB_human_C1-n1.pdb
│ │ ├── 1ICOS_6x4gA_human_V.pdb
│ │ ├── 1IL6Rb_1bquB_human_FN3-n2.pdb
│ │ ├── 1IL6Rb_1bquB_human_FN3-n3.pdb
│ │ ├── 1InsulinR_8guyE_human_FN3-n1.pdb
│ │ ├── 1InsulinR_8guyE_human_FN3-n2.pdb
│ │ ├── 1JAM1_1nbqA_human_Iset-n2.pdb
│ │ ├── 1LAG3_7tzgD_human_C1-n2.pdb
│ │ ├── 1LAG3_7tzgD_human_V-n1.pdb
│ │ ├── 1LaminAC_1ifrA_human.pdb
│ │ ├── 1MHCIa_7phrH_human_C1.pdb
│ │ ├── 1PD1_4zqkB_human_V.pdb
│ │ ├── 1PDL1_4z18B_human_V-n1.pdb
│ │ ├── 1Palladin_2dm3A_human_Iset-n1.pdb
│ │ ├── 1Sidekick2_1wf5A_human_FN3-n7.pdb
│ │ ├── 1Siglec3_5j0bB_human_C1-n2.pdb
│ │ ├── 1TCRa_6jxrm_human_C1-n2.pdb
│ │ ├── 1TCRa_6jxrm_human_V-n1.pdb
│ │ ├── 1Titin_4uowM_human_Iset-n152.pdb
│ │ ├── 1VISTA_6oilA_human_V.pdb
│ │ ├── 1VNAR_1t6vN_shark_V.pdb
│ │ ├── 1VTCN1_Q7Z7D3_human_C1-n2.pdb
│ │ ├── B2Microglobulin_7phrL_human_C1.pdb
│ │ ├── BTLA_2aw2A_human_Iset.pdb
│ │ ├── CD19_6al5A_human-n1.pdb
│ │ ├── CD28_1yjdC_human_V.pdb
│ │ ├── CD2_1hnfA_human_C2-n2.pdb
│ │ ├── CD2_1hnfA_human_V-n1.pdb
│ │ ├── CD3d_6jxrd_human_C1.pdb
│ │ ├── CD3e_6jxrf_human_C1.pdb
│ │ ├── CD3g_6jxrg_human_C2.pdb
│ │ ├── CD8a_1cd8A_human_V.pdb
│ │ ├── Contactin1_2ee2A_human_FN3-n9.pdb
│ │ ├── Contactin1_3s97C_human_Iset-n2.pdb
│ │ ├── ECadherin_4zt1A_human_n2.pdb
│ │ ├── FAB-HEAVY_5esv_C1-n2.pdb
│ │ ├── FAB-HEAVY_5esv_V-n1.pdb
│ │ ├── FAB-LIGHT_5esv_C1-n2.pdb
│ │ ├── FAB-LIGHT_5esv_V-n1.pdb
│ │ ├── GHR_1axiB_human_C1-n1.pdb
│ │ ├── ICOS_6x4gA_human_V.pdb
│ │ ├── IL6Rb_1bquB_human_FN3-n2.pdb
│ │ ├── IL6Rb_1bquB_human_FN3-n3.pdb
│ │ ├── InsulinR_8guyE_human_FN3-n1.pdb
│ │ ├── InsulinR_8guyE_human_FN3-n2.pdb
│ │ ├── JAM1_1nbqA_human_Iset-n2.pdb
│ │ ├── LAG3_7tzgD_human_C1-n2.pdb
│ │ ├── LAG3_7tzgD_human_V-n1.pdb
│ │ ├── LaminAC_1ifrA_human.pdb
│ │ ├── MHCIa_7phrH_human_C1.pdb
│ │ ├── PD1_4zqkB_human_V.pdb
│ │ ├── PDL1_4z18B_human_V-n1.pdb
│ │ ├── Palladin_2dm3A_human_Iset-n1.pdb
│ │ ├── Sidekick2_1wf5A_human_FN3-n7.pdb
│ │ ├── Siglec3_5j0bB_human_C1-n2.pdb
│ │ ├── TCRa_6jxrm_human_C1-n2.pdb
│ │ ├── TCRa_6jxrm_human_V-n1.pdb
│ │ ├── Titin_4uowM_human_Iset-n152.pdb
│ │ ├── VISTA_6oilA_human_V.pdb
│ │ ├── VNAR_1t6vN_shark_V.pdb
│ │ └── VTCN1_Q7Z7D3_human_C1-n2.pdb
│ ├── rename_structure_id.sh
│ ├── rmhet.js
│ ├── secondarystructure.js
│ ├── surfacearea.js
│ ├── tmalign-af.js
│ ├── tmalign-icn3dnode/
│ │ ├── Makefile
│ │ ├── TMalign.cpp
│ │ ├── igstrand2imgt.cpp
│ │ ├── igstrand2kabat.cpp
│ │ ├── resi2igstrand.cpp
│ │ ├── tmalignCgi.cpp
│ │ └── tmalignCgi.hpp
│ └── tmalign.js
├── icn3dpython/
│ ├── README.md
│ ├── icn3d_url/
│ │ ├── README.md
│ │ ├── batch_export_panel.py
│ │ ├── batch_export_png.py
│ │ ├── batch_export_refnum.py
│ │ └── batch_export_ss.py
│ └── web_scraping/
│ ├── README.md
│ ├── config.py
│ └── downloadInteraction.py
├── iframe.html
├── index.html
├── jupyternotebook/
│ ├── README.md
│ ├── icn3dpy/
│ │ └── __init__.py
│ └── setup.py
├── notfound.html
├── package.json
├── script/
│ ├── d3v4-force-all.js
│ ├── jszip.js
│ ├── mtz.js
│ ├── mtz.wasm
│ └── pako.js
├── share.html
├── share2.html
├── simple.html
├── src/
│ ├── html/
│ │ ├── alignSeq.js
│ │ ├── clickMenu.js
│ │ ├── dialog.js
│ │ ├── events.js
│ │ ├── html.js
│ │ ├── setDialog.js
│ │ ├── setHtml.js
│ │ └── setMenu.js
│ ├── icn3d/
│ │ ├── analysis/
│ │ │ ├── alignSW.js
│ │ │ ├── analysis.js
│ │ │ ├── applySymd.js
│ │ │ ├── cartoon2d.js
│ │ │ ├── delphi.js
│ │ │ ├── diagram2d.js
│ │ │ ├── dssp.js
│ │ │ ├── scap.js
│ │ │ └── symd.js
│ │ ├── annotations/
│ │ │ ├── addTrack.js
│ │ │ ├── annoCddSite.js
│ │ │ ├── annoContact.js
│ │ │ ├── annoCrossLink.js
│ │ │ ├── annoDomain.js
│ │ │ ├── annoIg.js
│ │ │ ├── annoPTM.js
│ │ │ ├── annoSnpClinVar.js
│ │ │ ├── annoSsbond.js
│ │ │ ├── annoTransMem.js
│ │ │ ├── annotation.js
│ │ │ ├── domain3d.js
│ │ │ ├── refnum.js
│ │ │ ├── showAnno.js
│ │ │ └── showSeq.js
│ │ ├── display/
│ │ │ ├── alternate.js
│ │ │ ├── applyCenter.js
│ │ │ ├── applyClbonds.js
│ │ │ ├── applyDisplay.js
│ │ │ ├── applyMissingRes.js
│ │ │ ├── applyOther.js
│ │ │ ├── applySsbonds.js
│ │ │ ├── camera.js
│ │ │ ├── draw.js
│ │ │ ├── fog.js
│ │ │ ├── legendTable.js
│ │ │ ├── scene.js
│ │ │ ├── setColor.js
│ │ │ ├── setOption.js
│ │ │ └── setStyle.js
│ │ ├── export/
│ │ │ ├── export3D.js
│ │ │ ├── saveFile.js
│ │ │ ├── shareLink.js
│ │ │ └── threeDPrint.js
│ │ ├── geometry/
│ │ │ ├── axes.js
│ │ │ ├── box.js
│ │ │ ├── brick.js
│ │ │ ├── cartoonNucl.js
│ │ │ ├── curve.js
│ │ │ ├── curveStripArrow.js
│ │ │ ├── cylinder.js
│ │ │ ├── glycan.js
│ │ │ ├── impostor.js
│ │ │ ├── instancing.js
│ │ │ ├── label.js
│ │ │ ├── line.js
│ │ │ ├── reprSub.js
│ │ │ ├── residueLabels.js
│ │ │ ├── sphere.js
│ │ │ ├── stick.js
│ │ │ ├── strand.js
│ │ │ ├── strip.js
│ │ │ └── tube.js
│ │ ├── highlight/
│ │ │ ├── hlObjects.js
│ │ │ ├── hlSeq.js
│ │ │ └── hlUpdate.js
│ │ ├── icn3d.js
│ │ ├── interaction/
│ │ │ ├── contact.js
│ │ │ ├── contactMap.js
│ │ │ ├── drawGraph.js
│ │ │ ├── getGraph.js
│ │ │ ├── hBond.js
│ │ │ ├── ligplot.js
│ │ │ ├── lineGraph.js
│ │ │ ├── piHalogen.js
│ │ │ ├── saltbridge.js
│ │ │ ├── showInter.js
│ │ │ └── viewInterPairs.js
│ │ ├── parsers/
│ │ │ ├── alignParser.js
│ │ │ ├── bcifParser.js
│ │ │ ├── ccp4Parser.js
│ │ │ ├── chainalignParser.js
│ │ │ ├── dcdParser.js
│ │ │ ├── densityCifParser.js
│ │ │ ├── dsn6Parser.js
│ │ │ ├── loadAtomData.js
│ │ │ ├── loadCIF.js
│ │ │ ├── loadPDB.js
│ │ │ ├── mmcifParser.js
│ │ │ ├── mmdbParser.js
│ │ │ ├── mol2Parser.js
│ │ │ ├── msaParser.js
│ │ │ ├── mtzParser.js
│ │ │ ├── opmParser.js
│ │ │ ├── parserUtils.js
│ │ │ ├── pdbParser.js
│ │ │ ├── realignParser.js
│ │ │ ├── sdfParser.js
│ │ │ ├── setSeqAlign.js
│ │ │ ├── vastplus.js
│ │ │ ├── xtcParser.js
│ │ │ └── xyzParser.js
│ │ ├── picking/
│ │ │ ├── control.js
│ │ │ ├── picking.js
│ │ │ └── ray.js
│ │ ├── selection/
│ │ │ ├── applyCommand.js
│ │ │ ├── definedSets.js
│ │ │ ├── firstAtomObj.js
│ │ │ ├── loadScript.js
│ │ │ ├── resid2spec.js
│ │ │ ├── selectByCommand.js
│ │ │ ├── selectCollections.js
│ │ │ └── selection.js
│ │ ├── surface/
│ │ │ ├── applyMap.js
│ │ │ ├── electronMap.js
│ │ │ ├── marchingCube.js
│ │ │ ├── proteinSurface.js
│ │ │ └── surface.js
│ │ └── transform/
│ │ ├── resizeCanvas.js
│ │ └── transform.js
│ ├── icn3dui.js
│ ├── thirdparty/
│ │ ├── CIFTools.js
│ │ ├── CIFToolsMod.js
│ │ ├── FileSaver.js
│ │ ├── canvas-to-blob.js
│ │ ├── color-pick/
│ │ │ ├── color-picker.css
│ │ │ └── color-picker.js
│ │ ├── defineWindow.js
│ │ ├── shader/
│ │ │ ├── CylinderImpostor.frag
│ │ │ ├── CylinderImpostor.vert
│ │ │ ├── CylinderInstancing.frag
│ │ │ ├── CylinderInstancing.vert
│ │ │ ├── Instancing.frag
│ │ │ ├── Instancing.vert
│ │ │ ├── NGL_Shaders.js
│ │ │ ├── SphereImpostor.frag
│ │ │ ├── SphereImpostor.vert
│ │ │ ├── SphereInstancing.frag
│ │ │ └── SphereInstancing.vert
│ │ └── three/
│ │ ├── OrthographicTrackballControls.js
│ │ ├── Projector.js
│ │ ├── StereoEffect.js
│ │ ├── TrackballControls.js
│ │ └── vr/
│ │ ├── ARButton.js
│ │ ├── CanvasKeyboard.js
│ │ ├── CanvasUI.js
│ │ ├── ControllerGestures.js
│ │ ├── GLTFLoader.js
│ │ ├── VRButton.js
│ │ ├── XRControllerModelFactory.js
│ │ └── motion-controllers.module.js
│ └── utils/
│ ├── convertTypeCls.js
│ ├── hashUtilsCls.js
│ ├── myEventCls.js
│ ├── parasCls.js
│ ├── rmsdSuprCls.js
│ ├── subdivideCls.js
│ └── utilsCls.js
└── template/
├── igstrand_template_IgC1.xlsx
├── igstrand_template_IgC2.xlsx
├── igstrand_template_IgI.xlsx
├── igstrand_template_IgV.xlsx
├── igstrand_template_IgV_A.xlsx
└── igstrand_template_IgV_A_Adash.xlsx
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules/
icn3dnpm/
tmpdir/
================================================
FILE: CHANGELOG.md
================================================
## Change Log
[icn3d-3.49.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.49.0.zip) was release on April 29, 2026. Added "2D Diagram for Nucleotides" using the R2DT diagram, and "2D Diagram for Ig Domains" with predetermined Ig templates. The features are available via the menu "Analysis > 2D Diagram" in iCn3D.
[icn3d-3.48.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.48.1.zip) was release on April 15, 2026. Fixed the issue in changing backgroound color.
[icn3d-3.48.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.48.0.zip) was release on April 14, 2026. The IgStrand numbering scheme for Ig-fold has been upgraded to version 1.2 by removing the templates for six IgE, seven IgFN3-like, and three other Igs.
[icn3d-3.47.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.47.1.zip) was release on January 21, 2026. Showed the count of hydrogen bonds over time for MD trajectories.
[icn3d-3.47.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.47.0.zip) was release on December 29, 2025. Added the support to load trajectory file (DCD or XTC) via the menu "File > Open File > MD Trajectory File", and draw a plane among three sets with the menu "Style > Plane among 3 Sets".
[icn3d-3.46.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.46.0.zip) was release on December 10, 2025. Added an external link "AI Tutor" in the Help menu to use AI as a tutor to show users step-by-step instructions about how to build a custom view with five methods: Interactive, Sharable URL, Jupyter Notebook, Node.js script, and Python script.
[icn3d-3.45.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.45.5.zip) was release on December 2, 2025. Fixed alternation display for aligned structures. Added a general Node.js script "general_id_cmd.js" to output results based on PDB/AlphaFold ID and commands from sharable URLs.
[icn3d-3.45.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.45.4.zip) was release on November 13, 2025. AlphaFold models were upgraded from version 4 to version 6, e.g., AF-Q12860-F1-model_v6.pdb.
[icn3d-3.45.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.45.3.zip) was release on September 22, 2025. Enabled to input long PDB IDs starting with pdb_0000. Fixed sequence alignment view and camera viewpoint-related issues.
[icn3d-3.45.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.45.2.zip) was release on August 20, 2025. Switched from Google Firebase Dynamic Link to our own short URL service; removed the line "global.THREE = require('three')" in iCn3D node.js scripts since three.js has been compiled into icn3d.js since iCn3D 3.44.0.
[icn3d-3.45.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.45.1.zip) was release on July 28, 2025. Fixed bugs in assigning specific template and VAST+ alignment sequence display.
[icn3d-3.45.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.45.0.zip) was release on July 10, 2025. Switched the transformation description in share links from trackball parameters to camera parameters used in BCF files. Fixed a bug in using node.js script.
[icn3d-3.44.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.2.zip) was release on June 30, 2025. Enabled to drag and drop to open mmCIF, PDB, PNG, and BCF files.
[icn3d-3.44.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.1.zip) was release on June 26, 2025. Output BCF viewpoint file via the menu "File > Save File > BCF Viewpoint".
[icn3d-3.44.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.44.0.zip) was release on June 11, 2025. Upgraded three.js to version 177 and compiled three.js directly into iCn3D library. No need to include three.js as a separate library.
[icn3d-3.43.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.43.0.zip) was release on May 27, 2025. Replaced Google Firebase dynamic links with our own short URL service.
[icn3d-3.42.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.42.0.zip) was release on May 12, 2025. Allowed to input MSA files (CLUSTALW or FASTA formats) to show both MSA and structure alignment via the menu "File > Open File > Multiple Seq. Alignment" in the "All Menus" mode; Added the "Stereo View" option in the menu "View"; allowed to save canvas video via the menu "File > Save File > Video"; added "NCBI" logo.
[icn3d-3.41.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.41.0.zip) was release on April 2, 2025. Added a menu at the top-left corner to allow users to choose simple or complete menus; added buttons at the top of the aligned sequences (menu Analysis > Aligned Seq.) to allow users to save the alignemnts in FASTA, CLUSTALW, or Residue by Residue formats.
[icn3d-3.40.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.40.5.zip) was release on March 20, 2025. Fixed the electron density map after switching from DSN6 to volume data from PDBe.
[icn3d-3.40.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.40.4.zip) was release on March 3, 2025. Fixed the URL for the "Side by Side" view.
[icn3d-3.40.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.40.3.zip) was release on February 13, 2025. Fixed DelPhi potential calculations for chains with more than one letter.
[icn3d-3.40.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.40.2.zip) was release on January 8, 2025. Collections update - fixed gz and zip functionality and collections parsing.
[icn3d-3.40.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.40.1.zip) was release on December 20, 2024. Collections update - added strucure append and clear commands, fix collections example directory.
[icn3d-3.40.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.40.0.zip) was release on December 11, 2024. Added the feature to show 2D Depiction/Diagram for chemical view with cid or smiles as input via the menu "Analysis > 2D Depiction"; fixed the positions of point lights relative to the camera.
[icn3d-3.39.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.39.0.zip) was release on November 25, 2024. Collections update - added multi file format support for zip, command history and JSON export, gz&ent file support.
[icn3d-3.38.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.38.1.zip) was release on November 14, 2024. Improved the conversion of chemical SMILES to 3D structure by showing double bonds.
[icn3d-3.38.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.38.0.zip) was release on November 12, 2024. Enabled to convert chemical SMILES to 3D structure via the menu "File > Retrieve by ID > Chemical SMILES".
[icn3d-3.37.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.37.0.zip) was release on October 16, 2024. Added a menu "Style > Clashed Residues > Hide" to hide clashed residues.
[icn3d-3.36.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.36.1.zip) was release on October 7, 2024. Fixed an issue related to previous chain IDs such as "A_1" (changed to "A1") in previous sharable links.
[icn3d-3.36.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.36.0.zip) was release on October 3, 2024. Allowed to load multiple iCn3D PNG images.
[icn3d-3.35.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.35.0.zip) was release on September 12, 2024. Retrieved updated ClinVar annotations directly from NCBI ClinVar database.
[icn3d-3.34.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.34.1.zip) was release on August 5, 2024. Switched VAST search alignment from backend to dynamic alignment in iCn3D.
[icn3d-3.34.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.34.0.zip) was release on July 29, 2024. Show atom details for the interaction of one ligand/residue with protein via the menu "Analysis > Interaction" and then the button "2D Interaction for One Ligand/Residue".
[icn3d-3.33.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.33.3.zip) was release on July 15, 2024. Switch electron density data from RCSB DSN6 to PDBe Density Server.
[icn3d-3.33.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.33.2.zip) was release on June 25, 2024. Enabled to measure angles between sets in the menu "Analysis > Angle".
[icn3d-3.33.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.33.1.zip) was release on June 18, 2024. Draw coils for the whole structure with zero radius for the secondary structure parts.
[icn3d-3.33.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.33.0.zip) was release on June 12, 2024. Users can use iCn3D to detect Ig domains for any structures and assign IgStrand reference numbers for each residue. The instruction is at https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html#igrefnum.
[icn3d-3.32.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.32.0.zip) was release on May 28, 2024. Allowed to load multiple mmCIF text files, which could contain multiple structures separated by "ENDMDL\n".
[icn3d-3.31.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.31.4.zip) was release on May 11, 2024. Release Ig templates for assigning reference numbers.
[icn3d-3.31.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.31.3.zip) was release on April 15, 2024. Jack Lin fixed some issues in loading a collection file.
[icn3d-3.31.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.31.2.zip) was release on April 9, 2024. Fixed the secondary structure ranges in CIF files.
[icn3d-3.31.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.31.1.zip) was release on March 21, 2024. Switched from "let xArray = atom_site.getColumn('Cartn_x').data" to "let xArray = atom_site.getColumn('Cartn_x')" when accessing data from Binary or text CIF data.
[icn3d-3.31.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.31.0.zip) was release on March 21, 2024. Replaced MMTF data with Binary CIF data. Both "mmtfid" and "bcifid" inputs get the Binary CIF data.
[icn3d-3.30.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.30.1.zip) was release on March 11, 2024. Fixed 3D domain summary view.
[icn3d-3.30.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.30.0.zip) was release on February 27, 2024. Calculate 3D domains on-the-fly and show 3D domains even when only one domain is available.
[icn3d-3.29.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.29.5.zip) was release on February 5, 2024. Added an option to load MTZ files from RCSB to show electrondensity maps; fixed a bug in saving sets; fixed a bug in the replay of rotating structures with a matrix.
[icn3d-3.29.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.29.4.zip) was release on January 19, 2024. Added the options to translate the coordinates (View > Translate XYZ) and rotate the coordinates with a matrix (View > Rotate with Matrix).
[icn3d-3.29.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.29.3.zip) was release on January 11, 2024. Fixed the "Sequences & Annotations" view when a collection of structure is loaded.
[icn3d-3.29.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.29.2.zip) was release on January 8, 2024. Simplified code to make icn3dnode calls simpler.
[icn3d-3.29.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.29.1.zip) was release on December 14, 2023. Allowed users to load a collection of structures via the menu "File > Open File > Collection File". The example collection file is at https://github.com/ncbi/icn3d/blob/master/example/collection.json.
[icn3d-3.29.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.29.0.zip) was release on December 13, 2023. Added the feature to load electron density maps from ccp4 or MTZ files either locally or via URLs.
[icn3d-3.28.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.28.4.zip) was release on November 7, 2023. Enabled to show DelPhi potential for multiple structures.
[icn3d-3.28.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.28.3.zip) was release on October 5, 2023. Fixed the async/await issue for electron density maps and EM density maps.
[icn3d-3.28.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.28.2.zip) was release on September 14, 2023. Upgraded three.js to version 151, and improved the image quality.
[icn3d-3.28.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.28.1.zip) was release on August 23, 2023. Added logs for menu usages, and updated iCn3D tutorials at https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html#videos.
[icn3d-3.28.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.28.0.zip) was release on July 31, 2023. Added the feature to show isoforms and exons as tracks with the button "Add Track" in the "Sequences & Annotations" window via the menu "Analysis > Sequences & Annotations". Also displayed pathogenic ClinVars in red and the rest in green in the ClinVar track.
[icn3d-3.27.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.27.0.zip) was release on July 13, 2023. Added the feature to search AlphaFold structures with protein/gene names directly in iCn3D via the menu "File > Search Structures > AlphaFold Structures".
[icn3d-3.26.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.26.1.zip) was release on July 10, 2023. Fixed a bug in TM-align and updated Python scripts.
[icn3d-3.26.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.26.0.zip) was release on June 20, 2023. Added the feature to run ESMFold on the fly via the menu "File > AlphaFold/ESM > ESMFold" in iCn3D.
[icn3d-3.25.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.25.4.zip) was release on June 15, 2023. Fixed the extra side chains of disulfide bonds when the disulfide bonds are not showing; fixed the extra tracks in Sequences & Annotations window.
[icn3d-3.25.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.25.3.zip) was release on May 24, 2023. Fixed a bug in Node.js scripts about retrieving data from Ajax calls.
[icn3d-3.25.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.25.2.zip) was release on May 17, 2023. Fixed a bug in VAST alignment.
[icn3d-3.25.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.25.1.zip) was release on May 3, 2023. Fixed a bug in relaigning structures.
[icn3d-3.25.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.25.0.zip) was release on April 26, 2023. To improve the performance of embedding several structures in a single page, added the URL parameter "imageonly=1" to render the structure into a static image instead of interactive 3D view. The image is clickable to launch an interactive 3D view.
[icn3d-3.24.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.24.2.zip) was release on April 24, 2023. Fixed the secondary structures of mutant in the mutation analysis; fixed the lower case in the sequences of VAST+ alignment.
[icn3d-3.24.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.24.1.zip) was release on April 14, 2023. Added more menus to the VR view of iCn3D to measure distance, toggle highlight, etc.
[icn3d-3.24.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.24.0.zip) was release on April 10, 2023. Improved the use of iCn3D in Jupyter Notebook by making the dialogs closable, and adding DelPhi potential and mutation analysis; added DelPhi potential as one option in the Virtual Reality (VR) view of iCn3D.
[icn3d-3.23.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.23.3.zip) was release on April 6, 2023. Fixed the VR view when only one controller is available.
[icn3d-3.23.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.23.2.zip) was release on April 2, 2023. Added the document at https://pypi.org/project/icn3dpy/ for loading a local/remote PDB file or iCn3D PNG Image into iCn3D in Jupyter Notebook; added both "Load" and "Append" options when launching a structure.
[icn3d-3.23.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.23.1.zip) was release on March 22, 2023. Added the feature to scale in AR view of iCn3D; changed the default coloring to "AlphaFold Confidence" when lining from BLAST to iCn3D for AlphaFold structures; added the wildcard symbol "*" for atom name selection.
[icn3d-3.23.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.23.0.zip) was release on March 16, 2023. Added more VR features to allow users to select residues, change style and color, and show interactions in VR. When users input PDB/AlphaFold IDs, the structures append to the existing structures.
[icn3d-3.22.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.22.4.zip) was release on March 7, 2023. Fixed the menu "View > Reset > All" for side chains. Added a new Node.js script "interactiondetail.js" to show the interaction details between two chains.
[icn3d-3.22.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.22.3.zip) was release on March 1, 2023. Fixed Node.js scripts cdsearch.js, and also fixed some iCn3D code to make Node.j scripts work.
[icn3d-3.22.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.22.2.zip) was release on February 23, 2023. Fixed a bug when the input is NCBI protein accession.
[icn3d-3.22.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.22.1.zip) was release on February 17, 2023. Enabled to align a sequence to a protein structure with similar sequence. If the protein accession is not a PDB chain, the corresponding AlphaFold UniProt structure is used. Added a Node.js script to add missing atoms to an exported PDB file.
[icn3d-3.22.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.22.0.zip) was release on February 14, 2023. Enabled to export PDB files with missing atoms or with hydrogen; added the option to show Multiple Sequence Alignment (MSA) when realigning multiple chains based on structure alignment.
[icn3d-3.21.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.21.3.zip) was release on February 3, 2023. Updated the description about custom reference numbers.
[icn3d-3.21.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.21.2.zip) was release on January 20, 2023. Added instructions (https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html#saveview) to host iCn3D PNG Images at any web server, which can be accessed by iCn3D, e.g., https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://api.figshare.com/v2/file/download/39125801, or https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://zenodo.org/api/files/1a3325c8-0c84-4f1e-be2c-c143b08c6563/3GVU-XCxR6fSTmXHxR3o1A.png.
[icn3d-3.21.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.21.1.zip) was release on January 17, 2023. Fixed a bug in selecting a conserved domain and a bug in the secondary structure display for pdbid input.
[icn3d-3.21.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.21.0.zip) was release on January 11, 2023. Mapped PDB residue numbers with NCBI residue numbers; replaced jQuery promise with JavaScript Promise, await, and async.
[icn3d-3.20.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.20.1.zip) was release on December 19, 2022. Removed unnecessary import and cleaned some codes.
[icn3d-3.20.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.20.0.zip) was release on December 12, 2022. Enabled users to show AlphaFold structures with NCBI RefSeq IDs as input.
[icn3d-3.19.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.19.1.zip) was release on December 1, 2022. Fixed bugs in retrieving data with the POST method, in aligning two chains from the same structure, in mutational analysis, and in launching several PDB/AlphaFold IDs.
[icn3d-3.19.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.19.0.zip) was release on November 16, 2022. Enabled users to add custom reference residue number with the menu "Analysis > Ref. Number", add (transparent) spheres or cubes for any sets with the menu "Style > Cartoon for a Set", add a line between two sets with the menu "Style > Line btw. Two Sets". Also improved the color legends.
[icn3d-3.18.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.18.1.zip) was release on November 8, 2022. Added the annotation "Transmembrane" for PDB or AlphaFold structures based on data from OPM, Membranome, and UniProt; enabled to load a PAE file from a URL in the same domain.
[icn3d-3.18.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.18.0.zip) was release on November 3, 2022. Merged Jack Lin's branch with the following new features: show some basic color legends when coloring a structure; add local Smith-Waterman alignment when aligning two sequences; add a preference in the menu "Style > Prefereces" to enlarge the command window.
[icn3d-3.17.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.17.0.zip) was release on October 27, 2022. Showed membranes for AlphaFold structures, which are single-spanning transmembrane proteins; enabled to load recent version of PAE json files for AlphaFold Structures.
[icn3d-3.16.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.16.3.zip) was release on September 29, 2022. Fixed a bug in VAST chain alignment. Updated the video of iCn3D tutorial.
[icn3d-3.16.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.16.2.zip) was release on September 19, 2022. Enabled to submit loaded structures to VAST and Foldseek to search similar structures directly.
[icn3d-3.16.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.16.1.zip) was release on September 12, 2022. Fixed a bug in VAST+ alignment based on TM-align.
[icn3d-3.16.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.16.0.zip) was release on September 8, 2022. Added dynamic VAST+ based on TM-align for any two structure assemblies in the menus "File > Align > Protein Complexes > Two PDB Structures" and "File > Realign Selection > Protein Complexes". Added the angle restriction for hydrogen bonds back.
[icn3d-3.15.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.15.1.zip) was release on August 18, 2022. Fixed the stickiness of menus and the TM-align output for a subset of residues.
[icn3d-3.15.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.15.0.zip) was release on August 16, 2022. Enabled users to switch between "All Menus" and "Simple Menus", or customize the menus and save them in a file to be loaded by others. The customized menus are sticky and saved in cache.
[icn3d-3.14.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.14.3.zip) was release on August 12, 2022. Enabled to show 200 million AlphaFold UniProt structures in the recent release.
[icn3d-3.14.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.14.2.zip) was release on August 9, 2022. Added the "FAQ" section in the "Help" menu.
[icn3d-3.14.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.14.1.zip) was release on August 3, 2022. Enabled to show mutation analysis for currently loaded structures or structures to be loaded with PDB IDs or AlphaFold UniProt IDs in the menu "Analysis > Mutation".
[icn3d-3.14.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.14.0.zip) was release on August 1, 2022. Added Post-Translational Modification (PTM) annotation from UniProt.
[icn3d-3.13.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.13.2.zip) was release on July 27, 2022. jQuery UI was upgraded from version 1.12.1 to 1.13.2.
[icn3d-3.13.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.13.1.zip) was release on July 25, 2022. Split the menu "File > Align > Multiple Chains" into three categories: by Structure Alignment, by Sequence Alignment, and Residue by Residue".
[icn3d-3.13.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.13.0.zip) was release on July 21, 2022. In addition to "VAST" alignment, users can also use "TM-align" to align two chains. The feature is available in the menus "File > Align > Multiple Chains" and "File > Realign Selection > by Structure Alignment".
[icn3d-3.12.8](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.8.zip) was release on July 15, 2022. Expanded Python scripts to download any output in iCn3D; Added a new Node.js script to retrieve all annotations (e.g., SNP, ClinVar, domain, etc) from iCn3D; added an exmaple to add users' own annotation and hide default annotations at https://github.com/ncbi/icn3d/tree/master/example/addAnnoLocal.html.
[icn3d-3.12.7](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.7.zip) was release on July 7, 2022. Fixed the iCn3D view from VAST search.
[icn3d-3.12.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.6.zip) was release on July 5, 2022. Enabled to load a mmCIF file from a URL in the menu "File > Open File > URL(CORS)".
[icn3d-3.12.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.5.zip) was release on July 1, 2022. Output the full mutant PDB file in mutation analysis, and fixed the alignment of same chain IDs.
[icn3d-3.12.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.4.zip) was release on June 21, 2022. Added some basic navigation controls in VR and AR views. In the VR view, users can use the right trigger to move forward and the left trigger to move backward. In the AR view, users can tap once on the screen to locate a minimized 3D structure in the tapped location, and tap twice quickly to scale up the 3D structure.
[icn3d-3.12.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.3.zip) was release on June 13, 2022. Users now can have Augmented Reality (AR) view on any iCn3D display by clicking the "START AR" button. Currently the AR view is only available to Chrome browser in an Android phone.
[icn3d-3.12.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.2.zip) was release on June 10, 2022. Made two kinds of transparent surfaces available in the menu "Style > Surface Opacity": "Fast Transparency" and "Slow Transparency".
[icn3d-3.12.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.1.zip) was release on June 8, 2022. Fixed the bug about exporting iCn3D PNG Image.
[icn3d-3.12.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.12.0.zip) was release on June 6, 2022. Users now can have Virtual Reality (VR) view on any iCn3D display; enabled to view multiple sequence alignment (MSA) instead of pairwise alignment; changed the tools in toolbar from buttons to icons; switched to WegGL2 from WebGL1.
[icn3d-3.11.8](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.8.zip) was release on May 20, 2022. Enabled to import "icn3d.css" and "three.module.js" from npm icn3d to work with React.
[icn3d-3.11.7](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.7.zip) was release on May 18, 2022. Enabled to import "icn3d.module.js" from npm icn3d to work with React.
[icn3d-3.11.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.6.zip) was release on May 11, 2022. Fixed a bug in the dynamic VAST alignment in iCn3D with the menu "File > Realign Selection > by Structure Alignment".
[icn3d-3.11.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.5.zip) was release on May 3, 2022. Added VAST+ and VAST Search interface in the menu "File > Search Similar". Enabled to assign rainbow/spectrum colors for a list of sets in the menu "Color > Rainbow/Spectrum > for Sets".
[icn3d-3.11.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.4.zip) was release on April 26, 2022. Added two examples in https://www.ncbi.nlm.nih.gov/Structure/icn3d/example.html to show the predefined alignment of two PDB files, or the predefined alignment of one PDB file to other chains.
[icn3d-3.11.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.3.zip) was release on April 22, 2022. Added the menu "Style > Nucl. Bases" to display the bases of nucleotides.
[icn3d-3.11.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.2.zip) was release on April 18, 2022. Added "[comment]" in front of logs to diferentiate from commands in the command window. If "mmdbid" is the input and the parameter "bu" is not defined, set "bu" as 1 (biological unit).
[icn3d-3.11.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.1.zip) was release on April 13, 2022. Users can input multiple PDB, MMDB, or AlphaFold IDs with the menu "File > Retrieve by ID > MMDB or AlphaFold IDs". Users can also load multiple PDB files at the same time with the menu "File > Open File > PDB Files (appendable)". The color legend became draggable.
[icn3d-3.11.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.11.0.zip) was release on March 28, 2022. Users can align AlphaFold structures or PDB structures with the menu "File > Align > Multiple Chains" or "File > Align > Protein Complexes > Two AlphaFold Structures". Users can also load any structures as usual, then load your custom PDB file with the menu "File > Open File > PDB File (appendable)", then relaign these structures with the menu "File > Realign Selection > by Structure Alignment".
[icn3d-3.10.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.10.1.zip) was release on March 9, 2022. Added the color option "Structure" and the style option "Hydrogens". Fixed some bugs on loading PDB files without headers.
[icn3d-3.10.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.10.0.zip) was release on March 7, 2022. Showed SNP and ClinVar annotations for AlphaFold structures.
[icn3d-3.9.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.9.0.zip) was release on February 28, 2022. Showed 3D domains for AlphaFold structures or any custom structures.
[icn3d-3.8.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.8.4.zip) was release on February 22, 2022. Enabled to export the content of any iCn3D dialog/popup window using a Python script in the command line. One example is at https://github.com/ncbi/icn3d/blob/master/icn3dpython/batch_export_panel.py.
[icn3d-3.8.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.8.3.zip) was release on February 17, 2022. Another paper about iCn3D was published in Frontiers (https://www.frontiersin.org/articles/10.3389/fmolb.2022.831740/full). Fixed some bugs as well.
[icn3d-3.8.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.8.2.zip) was release on February 7, 2022. Enabled to show mutations for AlphaFold structures in the menu "Analysis > Mutation". Fixed the 2D interaction network view of mutations.
[icn3d-3.8.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.8.1.zip) was release on January 28, 2022. Upgrade three.js from version 128 to 137 to avoid the security issues related to iframe.
[icn3d-3.8.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.8.0.zip) was release on January 27, 2022. Enabled to show the map of AlphaFold Predicted Aligned Error (PAE) on the fly in the menu "File > Retrieve by ID > AlphaFold UniProt ID", and load custom PAE files in the menu "File > Open File > AlphaFold PAE File". Switched the parameter "buidx" to "bu" for asymmetric units and biological units.
[icn3d-3.7.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.7.2.zip) was release on January 21, 2022. Now users can see all interactions, common interactions, and different interactions when several structures are aligned and their interactions are shown, e.g., https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?9FD78C7YsE9zKyi18.
[icn3d-3.7.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.7.1.zip) was release on January 19, 2022. Modified the Node.js example script to use the URL parameter "&bu=0" to get the asymmetric unit data from MMDB. Fixed the fog view when zooming in or out.
[icn3d-3.7.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.7.0.zip) was release on January 18, 2022. Now users can see the common interactions in several structures when they are aligned and their interactions are shown. iCn3D now shows asymmetric unit instead of biological unit since asymmetric unit contains all chains. Added the menu "File > Search Structure".
[icn3d-3.6.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.6.1.zip) was release on January 11, 2022. Enabled users to change the color for all lables in the menu "Analysis > Label > Change Label Color".
[icn3d-3.6.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.6.0.zip) was release on January 7, 2022. Added some example Python scripts in the directory "icn3dpython" to export secondary structures or PNG images. Changed the background color from "transparent/white" to "black".
[icn3d-3.5.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.5.1.zip) was release on December 27, 2021. Changed the canvas background color from "black" to "white". The "Style > Background" is still "transparent", but the display will be white by default.
[icn3d-3.5.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.5.0.zip) was release on December 21, 2021. Enabled to use the global Smith Waterman algorithm to align a sequence to a structure in the menu "File > Align > Sequence to Structure". Improved the labels by removing the background boxes and adjusting the text color when switching the canvas background color. Simplified the menus when the URL parameter "simplemenu=1" is used.
[icn3d-3.4.13](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.13.zip) was release on December 13, 2021. Added the menu "File > Save File > Selection Details" to export selected residues in a human readable format.
[icn3d-3.4.12](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.12.zip) was release on November 24, 2021. Fixed some bugs including the selection on 1D sequences in mobile devices.
[icn3d-3.4.11](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.11.zip) was release on November 19, 2021. Relaxed the angle restriction on hydrogen bonds and pi-stacking.
[icn3d-3.4.10](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.10.zip) was release on November 15, 2021. Fixed the base URL when launching iCn3D from a non-NCBI server.
[icn3d-3.4.9](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.9.zip) was release on November 8, 2021. Enabled to export secondary structure information for any subset of any structure, including AlphaFold UniProt structures. The feature is available in the menu "File > Save File > Secondary Structure".
[icn3d-3.4.8](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.8.zip) was release on October 22, 2021. Fixed the mapping between PDB residue numbers and NCBI residue numbers for 3D domains, conserved domains, and sites.
[icn3d-3.4.7](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.7.zip) was release on October 15, 2021. Fixed the 3D display of a single residue without any secondary structure, and fixed the selection of a chain in the sequence window.
[icn3d-3.4.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.6.zip) was release on October 6, 2021. Fixed the PDB file export for multiple structures.
[icn3d-3.4.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.5.zip) was release on September 28, 2021. Showed Conserved Domains for all structures in the Sequences & Annotations window.
[icn3d-3.4.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.4.zip) was release on September 16, 2021. Users can load multiple structures by appending PDB files with the menu "File > Open File > PDB File (appendable)" to othe PDB files or other structures, which are retrieved by IDs such as "mmdbid" or "mmmtfid". A new color method "Color > Hydrophobicity > Normalized" was added to show hydrophobicity for any residues with different green color.
[icn3d-3.4.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.3.zip) was release on September 10, 2021. Replaced delphi.fcgi with delphi.cgi.
[icn3d-3.4.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.2.zip) was release on August 26, 2021. AlphaFold predicted structures can be viewed at iCn3D with the menu "File > Retrieve by ID > AlphaFold UniProt ID". The domain annotation can be displayed by clicking the menu "Analysis > Seq. & Annotations".
[icn3d-3.4.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.1.zip) was release on August 23, 2021. Added a Python script to export PNG images for any structures in the batch mode. The script is in the directory icn3dnode.
[icn3d-3.4.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.4.0.zip) was release on August 19, 2021. Dynamically generate "2D Cartoon" in the chain, domain, or secondary structure levels for selected residues in the menu "Analysis". The cartoons are draggable and clickable.
[icn3d-3.3.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.3.5.zip) was release on August 11, 2021. Added a new color method "Rainbow" to show colors ranging from red to violet. This is different from the previous "Spectrum" method, which shows colors ranging from violet to red.
[icn3d-3.3.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.3.4.zip) was release on July 28, 2021. The license of scap for side chain prediction was waived by Dr. Barry Honig. Replaced 'var' with 'let' in iCn3D JavaScript files.
[icn3d-3.3.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.3.2.zip) was release on July 14, 2021. Now users can choose the color gradient and show the color legend for the "Custom Color" button in the Sequences & Annotations window.
[icn3d-3.3.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.3.1.zip) was release on July 7, 2021. Enabled to use as an input a URL containing a saved iCn3D PNG image in the menu "File > Open File > URL (CORS)". Fixed a bug in displaying an assembly with multiple copies of a structure.
[icn3d-3.3.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.3.0.zip) was release on June 30, 2021. Added the feature to show contact map for any selected residues in the menu "Analysis > Contact Map".
[icn3d-3.2.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.2.3.zip) was release on June 25, 2021. Enabled to set the URL parameter "menuicon" to 1 to show icons for those menus requiring internet access or license.
[icn3d-3.2.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.2.2.zip) was release on June 20, 2021. Skipped the secondary structure calculation when the input is a PDB file of nucleotides.
[icn3d-3.2.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.2.1.zip) was release on June 18, 2021. Export all classes so that functions in these classes can be manually modified as shown in the file example.html.
[icn3d-3.2.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.2.0.zip) was release on June 8, 2021. Added the option to load UniProt ID in the menu "File > Retrieve by ID > UniProt ID".
[icn3d-3.1.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.1.6.zip) was release on June 4, 2021. Fixed file export in 3D printing due to the change of Geometry to BufferGeometry in three.js version 128.
[icn3d-3.1.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.1.5.zip) was release on June 2, 2021. Added an option to load predefined alignments in the menu "File > Align > Multiple Chains". Enabled to change the shininess, lights, and thickness in the menu "Style > Preferences".
[icn3d-3.1.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.1.4.zip) was release on May 27, 2021. Remove "alert" in the npm icn3d package.
[icn3d-3.1.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.1.3.zip) was release on May 25, 2021. Switched residue numbers from integers to strings such as "100A".
[icn3d-3.1.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.1.2.zip) was release on May 17, 2021. iCn3D could hide the features requiring licenses, such as "Analysis > DelPhi Potential" and "Analysis > Mutaion" using the URL parameter "hidelicense=1".
[icn3d-3.1.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.1.1.zip) was release on May 10, 2021. Fixed the URL parameters in full.html.
[icn3d-3.1.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.1.0.zip) was release on May 7, 2021. Added icn3d.module.js in the build directory. Upgraded three.js from version 103 to version 128. THREE.Geometry was replaced with THREE.BufferGeometry.
[icn3d-3.0.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-3.0.0.zip) was release on May 3, 2021. iCn3D version 3.0.0 was converted to ES6 with classes and is available in npm with the package "icn3d". Users can use npm to install icn3d and generate Node.js scripts by calling icn3d functions. All previously embedded iCn3D will not be affected. To embed iCn3D version 3, iCn3D JavaScript and CSS library files were renamed from "icn3d_full_ui" to "icn3d". A global variable "icn3d" was used to access the class iCn3DUI: "var icn3dui = new icn3d.iCn3DUI(cfg)".
[icn3d-2.24.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.24.6.zip) was release on March 22, 2021. Broke large files into small ones and stopped upgrading the basic/simple UI.
[icn3d-2.24.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.24.5.zip) was release on March 4, 2021. Changed the default 3D view (https://www.ncbi.nlm.nih.gov/Structure/icn3d/index.html) from the basic view (https://www.ncbi.nlm.nih.gov/Structure/icn3d/simple.html) to the advanced view (https://www.ncbi.nlm.nih.gov/Structure/icn3d/full.html).
[icn3d-2.24.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.24.4.zip) was release on February 12, 2021. Added an option to save a sharable link with your note/window title in the menu "File > Share Link".
[icn3d-2.24.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.24.3.zip) was release on February 9, 2021. Added a few more Node.js scripts in icn3dnode to retrieve ligand-protein and protein-protein interactions, change of interactions, binding site and domain information in the command line.
[icn3d-2.24.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.24.2.zip) was release on February 3, 2021. Enabled to show multiple mutations together in the menu "Analysis > Mutation".
[icn3d-2.24.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.24.1.zip) was release on February 1, 2021. Enabled to show multiple mutations together ( https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?tKz5GiA2pTVQEWwy6). Fixed the alternation in multiple chain alignment.
[icn3d-2.24.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.24.0.zip) was release on January 27, 2021. Converted the interaction part of iCn3D to Node.js (in the directory icn3dnode) to allow batch-mode analysis. Expanded the chain-chain alignment to multiple chain alignment in the menu "File > Align > Multiple Chains". You could focus on part of the chains for the multiple chain alignment.
[icn3d-2.23.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.23.2.zip) was release on January 8, 2021. Adjust the chain IDs according to the data from the backend cgi that aligns a sequence to a structure.
[icn3d-2.23.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.23.1.zip) was release on January 6, 2021. Added version number and improved Principle Axes view.
[icn3d-2.23.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.23.0.zip) was release on December 22, 2020. Use PDB residue numbers when the input is MMDB ID.
[icn3d-2.22.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.22.2.zip) was release on December 15, 2020. Enabled to show secondary structures for a PDB file containing multiple structures.
[icn3d-2.22.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.22.1.zip) was release on December 1, 2020. Added cartoons for glycan display by default. The cartoons can be toggled in the menu "Style > Glycans".
[icn3d-2.22.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.22.0.zip) was release on November 24, 2020. Users now can alternate wild type and mutant, and their interaction networks in 3D for each SNP/ClinVar in the "Sequences & Annotation" window.
[icn3d-2.21.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.21.0.zip) was release on November 17, 2020. Symmetry can be calculated dynamically for selected residues using SymD. The menu is at "Analysis > Symmetry (SymD, dynamic)". Speeded up the loading of VAST+ structure alignment.
[icn3d-2.20.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.20.2.zip) was release on October 29, 2020. Fixed the DelPhi potential map in VAST+ alignment. Added back the usage tracking.
[icn3d-2.20.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.20.1.zip) was release on October 13, 2020. Reverted the label scale to 0.3. Updated the LICENSE.
[icn3d-2.20.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.20.0.zip) was release on October 6, 2020. Users can show electrostatic potential on surface or as equipotential map for any subsets of proteins/nucleotides/membrane/ligands. The PQR file (modified PDB file with partial charges and radii) can also be downloaded. The display of helices, tubes, and axes were improved. Users can change the helix display at the menu "Style > Two-color Helix".
[icn3d-2.19.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.19.1.zip) was release on September 15, 2020. Added shade to 3D display using multiple lights.
[icn3d-2.19.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.19.0.zip) was release on August 12, 2020. DelPhi potential map can be displayed for PDB structures at "Analysis > DelPhi Potential". The PDB file can be loaded in the URL with "pdbid=" or at "File > Open File". The DelPhi potential file can be calculated at DelPhi Web Server and be exported as a Cube file. The potential file can be accessed in a URL if it is located in the same host as iCn3D.
[icn3d-2.18.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.18.4.zip) was release on August 10, 2020. Fixed bugs in recently modified dropdown menus.
[icn3d-2.18.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.18.3.zip) was release on August 3, 2020. Moved "H-Bonds & Interactions" to "Analysis" menu and merged "Windows" menu with "Analysis" menu. Changed the default dialog color to blue. Users can change it at the menu "Style > Dialog Color".
[icn3d-2.18.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.18.2.zip) was release on July 28, 2020. Use strict mode and shrink code size.
[icn3d-2.18.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.18.1.zip) was release on July 23, 2020. Fixed the issue in the Jupyter Notebook widget icn3dpy; fixed the synchronized issue of the function show3DStructure().
[icn3d-2.18.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.18.0.zip) was release on July 20, 2020. Showed SNPs for SARS-CoV-2 proteins; generated icn3dpy (the Jupyter Notebook widget of iCn3D) at https://pypi.org/project/icn3dpy; added "2D Interaction Map" in the menu "View > H-Bonds & Interactions"; added the shrink/expand icon in each orange dialog/window.
[icn3d-2.17.7](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.7.zip) was release on July 7, 2020. Added iCn3D tutorial videos and slides.
[icn3d-2.17.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.6.zip) was release on June 29, 2020. All "Share Link" URLs can show the original view using the archived version of iCn3D at "File > Open File > Share Link URL in Fixed Ver.".
[icn3d-2.17.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.5.zip) was release on June 26, 2020. Made a versioned full_[version].html file so that "Share Link" URL could point to a fixed version by replacing "full.html" with "full_[version].html".
[icn3d-2.17.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.4.zip) was release on June 24, 2020. Users now can select options for cross structure interaction in alignment. Get ClinVar annotations from database.
[icn3d-2.17.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.3.zip) was release on June 18, 2020. Added the track "Cross-Linkages" for glycans, etc.
[icn3d-2.17.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.2.zip) was release on June 17, 2020. Added pinger to log usage.
[icn3d-2.17.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.1.zip) was release on June 15, 2020. Enabled to accept RID from BLAST result page.
[icn3d-2.17.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.17.0.zip) was release on June 12, 2020. Users can now show "2D Interaction Graph" and also "Highlight Interactions in Table" in the menu "View > H-Bonds & Interactions". Users can also replay the share link step by step to learn how to generate a custom display in the menu "File > Replay Each Step".
[icn3d-2.16.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.16.4.zip) was release on June 8, 2020. Enabled to turn force off in 2D Graph so that users can manually arrange the nodes.
[icn3d-2.16.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.16.3.zip) was release on June 5, 2020. Added halogen bonds, pi-cation, and pi-stacking interactions in the menu "View > H-Bonds & Interaction" and showed the interactions in sorted tables.
[icn3d-2.16.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.16.2.zip) was release on May 29, 2020. Added the following features: 1. save contents in any dialog/window using the save icon next to the closeicon, 2. set names for each tab/window using the menu "Windows > Your Note / Window Title", 3. select by property (such as residue type, solvent accessibilty, etc) in the "Select" menu, 4. Use a custom file to define the color or tube size for each residue by clicking the button "Custom Color / Tube" in the "Sequences & Annotations" window.
[icn3d-2.16.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.16.1.zip) was release on May 27, 2020. Enabled to show gaps when adding multiple sequence alignment data as tracks.
[icn3d-2.16.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.16.0.zip) was release on May 21, 2020. Enabled to show interactions using force-directed graph in the menu "View > H-Bonds & Interactions > Force-Directed Graph". Enabled to calculate Solvent Accessible Surface Area (SASA) in the menu "View > Surface Area", or color by SASA in the menu "Color > Solvent Accessibility". Fixed ClinVar and SNP annotations.
[icn3d-2.15.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.15.3.zip) was release on May 8, 2020. Enabled to update the short "Share Link" URL; enabled to save in a sharable URL the "Side by Side" view, which is useful to view two aligned structures.
[icn3d-2.15.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.15.2.zip) was release on May 5, 2020. Fixed the display of electron density map and EM map.
[icn3d-2.15.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.15.1.zip) was release on April 27, 2020. Improved the unionHash function to speed up selection. Improved the message when loading a list of commands.
[icn3d-2.15.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.15.0.zip) was release on April 21, 2020. Enabled to show the same structure "Side by Side" in two views in the "View" menu. Each view has the same orientation, but can have independent 3D display. Enabled to add multiple sequence alignments as tracks when clicking "Add Track" in the "Sequences & Annotations" window. Added "Hide Selection" in the "View" menu. Improved selection on "H-Bonds & Interactions". Improved the UI for "Realign Selection" in the "File" menu. The gallery shows COVID-19-related structures at the top.
[icn3d-2.14.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.14.0.zip) was release on April 15, 2020. Added the features to load Electron Density data in the menu "File > Open File > Electron Density", resize the 3D window, realign two structures in the menu "File > Realign", color residues with custom colors in the menu "Color > Residue > Custom", and add custom colors when aligning a sequence to a structure.
[icn3d-2.13.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.13.1.zip) was release on March 26, 2020. Showed membranes for transmembrane proteins in VAST+ alignment.
[icn3d-2.13.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.13.0.zip) was release on March 23, 2020. Added the "Symmetry" feature in the View menu.
[icn3d-2.12.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.12.1.zip) was release on March 12, 2020. Fixed a bug on hydrogen bonds between residues with the same residue number but different chain names. Removed duplicated names in the menu of "Defined Sets".
[icn3d-2.12.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.12.0.zip) was release on March 2, 2020. Enabled to realign selected residues in VAST+ structure alignment. The option "Realign Selection" is in the View menu.
[icn3d-2.11.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.11.5.zip) was release on Fearuary 11, 2020. Add colors to aligned sequence track based on Blosum62.
[icn3d-2.11.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.11.4.zip) was release on January 14, 2020. The compiling tool gulp was upgraded to version 4.
[icn3d-2.11.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.11.3.zip) was release on January 7, 2020. A bug was fixed to have predefined sets available for hydrogen bonds/interations.
[icn3d-2.11.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.11.2.zip) was release on December 20, 2019. Added the style Backbone and added 'use strict' to each function.
[icn3d-2.11.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.11.1.zip) was release on December 19, 2019. Fixed a bug in chain alignment due to the introduction of membranes.
[icn3d-2.11.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.11.0.zip) was release on December 16, 2019. Enabled to show membranes for transmembrane proteins for data from PDB or MMDB by aligning the coordinates to the data from Orientations of Proteins in Membranes (OPM) database. Users could adjust the location of the membrane as well.
[icn3d-2.10.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.10.1.zip) was release on December 9, 2019. Enabled to select the currently displayed set in the Select menu and fixed some bugs on H-Bonds & Interactions.
[icn3d-2.10.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.10.0.zip) was release on December 5, 2019. Enabled to show each hydrogen bond and contact in 3D using the menu "View > H-Bonds & Interactions".
[icn3d-2.9.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.9.2.zip) was release on November 18, 2019. Added Transmembrane track if the input is opmid. Added angle constraint for hydrogen bonds.
[icn3d-2.9.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.9.1.zip) was release on November 14, 2019. Enable to select regions between two X-Y membranes for transmembrane proteins.
[icn3d-2.9.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.9.0.zip) was release on November 8, 2019. Display membranes for transmembrane proteins using data from Orientations of Proteins in Membranes (OPM). The feature is at "File > Retrieve by ID > OPM PDB ID".
[icn3d-2.8.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.8.3.zip) was release on November 5, 2019. Display/output salt bridges; color helices and sheets with spectrum in the menu "Color > Secondary > Spectrum".
[icn3d-2.8.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.8.2.zip) was release on October 29, 2019. Reduced the size of three.js (version 103) library. Added links to dbSNP in the mouseover texts of SNP annotations.
[icn3d-2.8.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.8.1.zip) was release on October 21, 2019. Fixed the 2D interaction display in structure alignment. The bug was introduced in the last release.
[icn3d-2.8.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.8.0.zip) was release on October 10, 2019. Allowed to align any chain to another chain in the menu "File > Align > Chain to Chain".
[icn3d-2.7.19](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.19.zip) was release on September 9, 2019. Fixed the input width and height with "%".
[icn3d-2.7.18](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.18.zip) was release on September 4, 2019. Added the option to view in Full Screen mode by clicking the expansion icon in the top-right corner when "mobilemenu" is turned on, or by clicking "Full Screen" in the View menu.
[icn3d-2.7.17](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.17.zip) was release on September 3, 2019. Made it easy to save interactive work by clicking "File > Save File > iCn3D PNG Images". This saves both "iCn3D PNG Image" and an HTML file with a clickable PNG image, which is link to the custom display via a sharable link.
[icn3d-2.7.16](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.16.zip) was release on August 29, 2019. Fixed the transparent display by switching three.js from version 107 to 103.
[icn3d-2.7.15](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.15.zip) was release on August 21, 2019. Added an example page to embed multiple iCn3D viewers in one page.
[icn3d-2.7.14](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.14.zip) was release on August 21, 2019. Enabled the mobile style menu with the URL parameter "mobilemenu=1".
[icn3d-2.7.13](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.13.zip) was release on August 14, 2019. Fixed the calculations on contacting atoms by considering the centers of atoms and not radii of atoms. Minimized the code size by retrieving some rarely used code on the fly.
[icn3d-2.7.12](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.12.zip) was release on August 8, 2019. The backend to retrieve ClinVar annotation was fixed.
[icn3d-2.7.11](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.11.zip) was release on August 6, 2019. Added "Label Scale" in the View menu to scale all labels.
[icn3d-2.7.10](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.10.zip) was release on August 5, 2019. Improved the display of binding sites with fog and slab.
[icn3d-2.7.9](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.9.zip) was release on August 1, 2019. Fixed the effect of Fog on sticks and spheres.
[icn3d-2.7.8](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.8.zip) was release on July 31, 2019. Fixed SNP annotation in the sequences and annotations window.
[icn3d-2.7.7](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.7.zip) was release on July 30, 2019. Added the option to show or hide hydrogens when displaying PubChem compounds.
[icn3d-2.7.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.6.zip) was release on July 18, 2019. Enabled to show disulfide bonds when a custom pdb file is input; added the option to output pairs for disulfide bonds, hydrogen bonds, and interacting/contacting residues by distance.
[icn3d-2.7.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.5.zip) was release on July 16, 2019. Added a Gallery section. Fixed the picking and centering issues in some Mac computers. Optimized the width and height of embedded iCn3D viewer.
[icn3d-2.7.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.4.zip) was release on July 8, 2019. Included Miniland1333's fix on clickTab for the basic display; auto-detected lipids and treated them as chemicals; improved the display of modified PDB files; mouseover showed the structure names when there are more than one structures.
[icn3d-2.7.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.3.zip) was release on June 6, 2019. Improved the display of transparent surfaces.
[icn3d-2.7.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.2.zip) was release on June 5, 2019. Fixed the display of transparent surfaces.
[icn3d-2.7.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.1.zip) was release on June 3, 2019. Fixed the sequence display when there are insertion codes or missing coordinates.
[icn3d-2.7.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.7.0.zip) was release on May 20, 2019. "Share Link" can be used to reproduce a custom display when the input is a known ID. "iCn3D PNG Image" can be saved and opened in the File menu to reproduce a custom display for all cases, even when the input is a PDB file or other files.
[icn3d-2.6.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.6.6.zip) was release on May 16, 2019. The sequence display was fixed when the input is a MMTF ID or a mmCIF ID.
[icn3d-2.6.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.6.5.zip) was release on May 14, 2019. The sequence display was fixed when the input is a pdb file.
[icn3d-2.6.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.6.4.zip) was release on April 29, 2019. jQuery was upgraded to version 3.4.0.
[icn3d-2.6.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.6.3.zip) was release on April 23, 2019. Showed SNP annotations for more 3D structures.
[icn3d-2.6.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.6.2.zip) was release on April 15, 2019. Enabled to show large structure such as HIV-1 capsid (3J3Q): (https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmtfid=3j3q).
[icn3d-2.6.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.6.1.zip) was release on April 1, 2019. Enabled to link from BLAST result page to iCn3D.
[icn3d-2.6.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.6.0.zip) was release on March 19, 2019. Users can now align any sequence to a hit structure by clicking "Align > Sequence to Structure" in the File menu. The default color scheme is color by sequence "Conservation" for sequence-structure or structure-structure alignments.
[icn3d-2.5.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.5.3.zip) was release on March 12, 2019. Added commands for up arraow and down arrow after picking a residue.
[icn3d-2.5.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.5.2.zip) was release on March 5, 2019. The style Lines was fixed by replacing THREE.Line with THREE.LineSegments.
[icn3d-2.5.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.5.1.zip) was release on February 14, 2019. Change log was moved to the file CHANGELOG.md. Share Link was changed from https://d55qc.app.goo.gl/### to https://icn3d.page.link/###. All previous share links still work. iCn3D library file was renamed from full_ui_all_#.#.#.min.js to icn3d_full_ui_#.#.#.min.js. Both files are available to make it backward compatible.
[icn3d-2.5.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.5.0.zip) was release on January 31, 2019. Updated Three.js from version 80 to version 99. Enabled the basic version (simple_ui_all.min.js) to hide the Tools menu and title. Fixed a bug in picking an atom for distance or labeling.
[icn3d-2.4.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.4.3.zip) was release on January 22, 2019. Non-standard proteins or nucleotides were still displayed as "Biopolymer" in 2D interactions and were displayed in the style of protein or nucleotide in 3D. The usage tracking was implemented in iCn3D.
[icn3d-2.4.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.4.2.zip) was release on January 16, 2019. Non-standard proteins or nucleotides were displayed as "Biopolymer" in 2D interactions and were displayed in the style of "Stick" in 3D. A new kind of annotation "Disulfie Bonds" was added to the "Sequences and Annotations" window.
[icn3d-2.4.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.4.1.zip) was release on January 7, 2019. Enabled users to show EM density map for any subset of an EM structure.
[icn3d-2.4.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.4.0.zip) was release on December 17, 2018. Enabled users to show electron density map for any subset of a crystal structure.
[icn3d-2.3.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.3.4.zip) was release on December 12, 2018. Enabled users to load a saved iCn3D PNG image into iCn3D to reproduce the display using the URL embedded in the image.
[icn3d-2.3.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.3.3.zip) was release on December 6, 2018. Made the list of interacting residues consistent in "File -> Save File -> Interaction List" and in the "Sequences and Annotations" window.
[icn3d-2.3.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.3.2.zip) was release on October 30, 2018. Water molecules were enabled to be shown when the structure is not a biological assembly. Gene symbols were shown for each chain in the "Sequences and Annotations" window.
[icn3d-2.3.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.3.1.zip) was release on October 25, 2018. The color of the the first residue in a coil was fixed.
[icn3d-2.3.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.3.0.zip) was release on October 18, 2018. Added set operations (union,intersection, exclusion) in "Defined Sets"; added buttons "Helix Sets" and "Sheet Sets" in the "Sequences and Annotations" window to define helix sets and sheet sets in the window "Defined Sets"; added "Save Color" and "Apply Saved Color" in the menu "Color"; added "Save Style" and "Apply Saved Style" in the menu "Style"; added "Side Chains" in the menu "Select" to select side chains; added two options for color by "Secondary" structures: "Sheets in Green" and "Sheets in Yellow"; added color by "B-factor" that is normalized with "Original" values or "Percentile" values.
[icn3d-2.2.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.2.5.zip) was release on September 17, 2018. A bug in loading local PDB file was fixed.
[icn3d-2.2.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.2.4.zip) was release on September 6, 2018. The location of 2D interaction dialog was optimized.
[icn3d-2.2.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.2.3.zip) was release on August 30, 2018. Added an option to show N- and C-terminal labels.
[icn3d-2.2.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.2.2.zip) was release on August 9, 2018. Defined sets can be combined using "or", "and", and "not".
[icn3d-2.2.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.2.1.zip) was release on August 3, 2018. Mouseover on the 3D structure shows the residue or atom name. Some Ajax calls are combined into one Ajax call.
[icn3d-2.2.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.2.0.zip) was release on July 30, 2018. The smoothing algorithm was switched from Catmull-Rom spline to cubic spline to make the curves more smooth. The thickness of ribbon was decreased to make the sides of the ribbons less apparent. The radio buttons in the menus was replaced by the check sign. A "Save Image" button was added in the "Toolbar".
[icn3d-2.1.8](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.8.zip) was release on July 12, 2018. Checked the code with the strict mode.
[icn3d-2.1.7](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.7.zip) was release on June 28, 2018. Simplified the addition of custom text as a track in the Sequences and Annotations window.
[icn3d-2.1.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.6.zip) was release on June 21, 2018. A color picker was added to the color menu.
[icn3d-2.1.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.5.zip) was release on June 18, 2018. 3D printing are enabled for biological assemblies.
[icn3d-2.1.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.4.zip) was release on June 7, 2018. The retrieval of transformation matrix from mmCIF was fixed for Mac.
[icn3d-2.1.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.3.zip) was release on May 30, 2018. "Sequences and Annotations" is now able to be highlighted even if some annotations didn't show up.
[icn3d-2.1.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.2.zip) was release on May 23, 2018. The surface display was improved by adding light reflection. Light was added to the display of instanced biological assemblies.
[icn3d-2.1.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.1.zip) was release on May 22, 2018. The option of color by "Spectrum" was added back.
[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.
[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.
[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.
[icn3d-2.0.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.0.1.zip) was release on April 23, 2018. The bug about extra 3D domains in the "Sequences & Annotations" window was fixed. The stabilizers for 3D printing were improved.
[icn3d-2.0.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.0.0.zip) was release on April 17, 2018. By clicking the menu "Windows: View Sequences & Annotations", users can view all kinds of annotations: ClinVar, SNPs, CDD domains, 3D domains, binding sites, interactions, and custom tracks. Users can click the menu "View: Chemical Binding" to show the chemical binding sites. Users can also export files for 3D printing at the menu "File: 3D Printing: VRML (Color, W/ Stabilizers)".
[icn3d-1.4.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.4.1.zip) was release on November 3, 2017. The version of THREE.js in the zip file was fixed.
[icn3d-1.4.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.4.0.zip) was release on November 2, 2017. The rendering speed has been significantly improved by using the Imposter shaders from NGL Viewer. A bug in "Share Link" was fixed.
[icn3d-1.3.10](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.10.zip) was release on October 27, 2017. The "Save File" issue in Chrome 60 and after was fixed.
[icn3d-1.3.9](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.9.zip) was release on September 5, 2017. The handling of residues with insertion codes was fixed in structure alignment.
[icn3d-1.3.8](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.8.zip) was release on August 7, 2017. The handling of residues with insertion codes was fixed.
[icn3d-1.3.7](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.7.zip) was release on April 18, 2017. A bug in the output order of commands was fixed.
[icn3d-1.3.6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.6.zip) was release on April 10, 2017. A bug introduced in the version of icn3d-1.3.5 was fixed in the function unionHash.
[icn3d-1.3.5](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.5.zip) was release on March 23, 2017. The codes were optimized to show 3D structures as soon as possible. Vast+ structure alignment was optimized as well.
[icn3d-1.3.4](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.4.zip) was release on March 1, 2017. The backend of structure alignment was updated.
[icn3d-1.3.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.3.zip) was release on November 15, 2016. Now users can save the image with "transparent" background using a single url, e.g., [](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbid=1tup&width=300&height=300&command=set%20background%20transparent;%20export%20canvas)https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbid=1tup&width=300&height=300&command=set%20background%20transparent;%20export%20canvas.
[icn3d-1.3.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.2.zip) was release on October 18, 2016. The atom specification in "Advanced set selection" was modified to use "$" instead of "#" in front of structure IDs. This modification avoids to the problem of showing multiple "#" in the urls of "Share Link".
[icn3d-1.3.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.3.1.zip) was release on October 4, 2016. Partial diplay of helices or beta-sheets are enabled. The side chains, if displayed, are connected to C-alphas.
[icn3d-1.2.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.2.3.zip) was release on September 13, 2016. The MMTF format started to support https.
[icn3d-1.2.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.2.2.zip) was release on August 18, 2016. Added a switch button to switch between all atoms and selected atoms. When the mode is "selected atoms", the switch and the text "selection" next to it are colored in orange. The menu "Style", "Color", and "Surface" are colored in orange and only apply to selected atoms.
[icn3d-1.2.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.2.1.zip) was release on August 18, 2016. Some bugs were fixed.
[icn3d-1.2.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.2.0.zip) was release on August 17, 2016. The dialog of 2D interactions was added to show the interactions among different chains. Both the nodes (chains) and lines (interactions) can be selected. Secondary structures will be calculated if the input PDB file has no defined secondary structure information. The previous files src/icn3d.js, src/full_ui.js, and src/simple_ui.js were separated into small files.
[icn3d-1.1.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.1.1.zip) was release on July 25, 2016. Some bugs were fixed.
[icn3d-1.1.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.1.0.zip) was release on July 18, 2016. The new binary MMTF file format was supported. A new "Analysis" menu was added with an option to show disulfide bonds. Users can also input data from a url, either through the UI or through a encoded url parameter "?type=pdb&url=...", e.g., [](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=pdb&url=https%3A%2F%2Ffiles.rcsb.org%2Fview%2F1gpk.pdb)https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=pdb&url=https%3A%2F%2Ffiles.rcsb.org%2Fview%2F1gpk.pdb.
[icn3d-1.0.1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.0.1.zip) was release on May 16, 2016.
[icn3d-1.0.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-1.0.0.zip) was release on April 28, 2016.
The beta version [icn3d-0.9.6-dev](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-0.9.6-dev.zip) was release on April 21, 2016. Enabled to export and import selection file where each custom sets of atoms are defined. Javascript files and CSS files are versioned. Developers can use the default latest version or specify the specific version in their pages.
The beta version [icn3d-0.9.5-dev](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-0.9.5-dev.zip) was release on April 4, 2016. Enabled to import Mol2, SDF, XYZ, PDB, and mmCIF files. Added "Schematic" style for chemicals. Improved the coordination between pk on 3D structure and selection on sequences.
The beta version [icn3d-0.9.4-dev](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-0.9.4-dev.zip) was release on March 14, 2016. Added "Fog" and "Slab" features.
The beta version [icn3d-0.9.3-dev](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-0.9.3-dev.zip) was release on March 9, 2016. Improved the following features: "Back" and "Forward" button, Export State, Open State.
The beta version [icn3d-0.9.2-dev](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-0.9.2-dev.zip) was release on March 4, 2016. CSS namespace was added. The file simple_ui.js was reorganized to share some codes with full_ui.js. A "Schematic" style was added to show one letter residue name in the C-alpha (for protein) or O3' (for nucleotide) position.
The beta version [icn3d-0.9.1-dev](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-0.9.1-dev.zip) was release on Feb 9, 2016. The surface generation was switched from the iview version (surface.js) to the more efficient 3Dmol version (ProteinSurface4.js and marchingcube.js).
The beta version [icn3d-0.9.0-dev](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-0.9.0-dev.zip) was release on Jan 17, 2016.
================================================
FILE: CODE_OF_CONDUCT.md
================================================
## Contributor Code of Conduct
### Purpose
Our goal is to foster an open and welcoming community where contributors from all backgrounds can collaborate in a respectful and constructive manner. This Code of Conduct outlines our expectations for all those who participate in our project and the consequences for unacceptable behavior.
### Our Pledge
In the interest of fostering an open and welcoming community, we as contributors and maintainers pledge to make participation in our project and community a harassment-free experience for everyone, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
### Our Standards
Examples of behavior that contributes to a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy toward other community members
Examples of unacceptable behavior include:
* Harassment, discrimination, or bullying of any kind
* Inappropriate sexual language
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others’ private information without explicit permission
### Our Responsibilities
Project maintainers are responsible for:
* Clarifying the standards of acceptable behavior
* Taking appropriate and fair corrective action in response to any instances of unacceptable behavior
* Maintaining the confidentiality of anyone reporting incidents
### Scope
This Code of Conduct applies within all project spaces and also applies when an individual is representing the project or community in public spaces.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1.
================================================
FILE: LICENSE.md
================================================
iCn3D incorporates the DelPhi code to dynamically show the
electrostatic potential map. The DelPhi code is licensed by Columbia
University, which does not permit commercial use without contacting the
DelPhi project for permission.
The remaining code is a "United States Government Work" and is provided
by the terms described below:
PUBLIC DOMAIN NOTICE
National Center for Biotechnology Information
This software/database is a "United States Government Work" under the
terms of the United States Copyright Act. It was written as part of
the author's official duties as a United States Government employee and
thus cannot be copyrighted. This software/database is freely available
to the public for use. The National Library of Medicine and the U.S.
Government have not placed any restriction on its use or reproduction.
Although all reasonable efforts have been taken to ensure the accuracy
and reliability of the software and data, the NLM and the U.S.
Government do not and cannot warrant the performance or results that
may be obtained by using this software or data. The NLM and the U.S.
Government disclaim all warranties, express or implied, including
warranties of performance, merchantability or fitness for any particular
purpose.
Please cite the author in any work or product based on this material.
================================================
FILE: README.md
================================================
# iCn3D Structure Viewer
## [AI Tutor for iCn3D](https://vizomics.org/ai-tutor): shows step-by-step instructions about how to build a custom view
## About iCn3D
"I see in 3D" (iCn3D) Structure Viewer is not only a web-based 3D viewer, but also a structure analysis tool interactively or in the batch mode using NodeJS scripts based on the npm package icn3d. iCn3D synchronizes the display of 3D structure, 2D interaction, and 1D sequences and annotations. Users' custom display can be saved in a short URL or a PNG image. The complete package of iCn3D including Three.js and jQuery is in the directory "dist" after you get the source code with the "Code" button. You can click the file "index.html" in the "dist" directory to launch a local version of iCn3D.
* View a 3D structure in iCn3D:
Open the link [https://www.ncbi.nlm.nih.gov/Structure/icn3d](https://www.ncbi.nlm.nih.gov/Structure/icn3d), input a PDB ID, and click "Load". You can also click "File" menu to "Open File" to load PDB files or MD trajectories, or input other IDs.
As mentioned in the menu "Help > Transformation Hints", you can use Left mouse button for rotation, Middle mouse wheel for zooming, and Right mouse button for translation.
The most important point about using iCn3D is the current selection. Any operations on color, style, etc. are working on the current selection. By default, all atoms are selected. Once you select any subset, your operation will work ONLY on the subset. You can switch the selection using the toggle next to the Help menu.
* VR and AR views in iCn3D:
The Virtual Reality (VR) and Augmented Reality (AR) views are shown in this [video](https://youtu.be/XvjiK5bOtd0).
You can open a bowser in your Virtual Reality (VR) headset and view a 3D structure in iCn3D. Then click the button "Enter VR" at the bottom center of your browser to enter the VR view. You can select residues with the trigger button, open the menu with the squeeze button and click menus with the trigger, navigate with the thumbstick pressed forward/backward and press the trigger. There are menus for Select, Style, Color, and Analysis. You need to make one selection before clicking the Interaction button and make two selections before clicking the Distance button.
The Augmented Reality (AR) view is currently only available to iCn3D views in Chrome browser using Android phones. You can view a 3D structure in iCn3D and click the button "START AR" at the bottom center to see the 3D structure in your surroundings. You can tap twice quickly on the screen to locate a minimized 3D structure in your tapped location, and pinch to scale the 3D structure.
* Create custom 3D view:
You first open a structure in "File" menu, then select a subset in "Select" menu, view only the selected subset by clicking "View Only Selection" in View menu, finally change styles or colors in "Style" and "Color" menus.
Each operation has a corresponding command as listed at https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html#commands. These commands will show up in the command/log window right beneath the 3D display. To view all previous commands, you can click "Share Link" in "File" menu. Both the original URL and the short URL can be used to display your custom view.
* Save your work:
You can save "iCn3D PNG Image" in the menu "File > Save File". Both the PNG file and an HTML file are saved. Click the HTML file to see the PNG image, which is linked to the custom display via a shorten URL. The downloaded "iCn3D PNG Image" itself can also be used as an input in the menu "File > Open File" to reproduce the custom display. You can combine these HTML files to generate your own galleries.
The "iCn3D PNG Image" can also be stored in a web server (e.g., [https://figshare.com](https://figshare.com), [https://zenodo.org](https://zenodo.org)). The PNG image can then be loaded into iCn3D via the URL, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://api.figshare.com/v2/file/download/39125801](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://api.figshare.com/v2/file/download/39125801), or [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://zenodo.org/api/files/1a3325c8-0c84-4f1e-be2c-c143b08c6563/3GVU-XCxR6fSTmXHxR3o1A.png](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://zenodo.org/api/files/1a3325c8-0c84-4f1e-be2c-c143b08c6563/3GVU-XCxR6fSTmXHxR3o1A.png), where the URL of the PNG image is retrieved from the JSON blob at https://zenodo.org/api/records/7599970.
You can also save "Share Link" in "File" menu to share with your colleagues. These URLs are lifelong. You can click "Replay Each Step > On" in "File" menu to learn how a [custom display](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?u7gp4xS9rn4hahcLA) was generated. (Note: Due to the retirement of Google Firebase Dynamic Link, any short URL containing "https://icn3d.page.link/" should be replaced with "https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?". For example, "https://icn3d.page.link/2rZWsy1LZmtTS3kBA" should be replaced with "https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?2rZWsy1LZmtTS3kBA".)
All "Share Link" URLs can show the original view using the archived version of iCn3D by clicking "Open File > Share Link in Archived Ver." in "File" menu.
* Python scripts to batch process structures:
Python scripts can be used to process 3D structures (e.g., export secondary structures, PNG images, or analysis output) in batch mode. The example scripts are at [icn3dpython](https://github.com/ncbi/icn3d/tree/master/icn3dpython).
* Node.js scripts using npm "icn3d" to batch process structures:
You can download [npm "icn3d" package](https://www.npmjs.com/package/icn3d) to write Node.js scripts by calling iCn3D functions. These scripts can be used to process 3D structures (e.g., calculate interactions) in batch mode. The example scripts are at [icn3dnode](https://github.com/ncbi/icn3d/tree/master/icn3dnode).
* Annotations for AlphaFold structures:
For any custom structures such as AlphaFold structures, you can show [conserved domain and 3D domain annotations](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?bPSkpeshtiH1TxbP8). For AlphaFold structures, you can also show [SNP and ClinVar annotations](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?XSQ5oqDCTfEQ3iAY7).
* Align AlphaFold structures:
You can align [AlphaFold structures or PDB structures](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?chainalign=P69905_A,P01942_A,1HHO_A&showalignseq=1&bu=0) with the menu "File > Align > Multiple Chains" or "File > Align > Protein Complexes > Two AlphaFold Structures". You can also load any structures as usual, then load your custom PDB file with the menu "File > Open File > PDB File (appendable)", then relaign these structures with the menu "File > Realign Selection > by Structure Alignment".
* Alternate SNPs in 3D:
You can [alternate in 3D wild type and mutant of SNPs](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?fNpzDuUE287SBFtz8) by clicking the menu "Analysis > Sequences & Annotations", the tab "Details", the checkbox "SNP", and mouseover on SNPs.
* DelPhi Electrostatic Potential:
You can view the [DelPhi Electrostatic Potential](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?31DFceJiYw7SfStQA) in the menu "Analysis > DelPhi Potential".
* Isoforms and Exons:
You can view the [Isoforms and Exons](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?pA3pPu7LxdiuZDVX7) by clicking the button "Add Track" in the "Sequences & Annotations" window via the menu "Analysis > Sequences & Annotations".
* Multiple Sequence Alignment (MSA) Input:
You can input a MSA file (CLUSTALW or FASTA format) into iCn3D via the menu "File > Open File > Muleiple Seq. Alignment" in the "All Menus" mode. The view can be shared with others, e.g., [GPCR MSA](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?zvKpsn7PPJG4QEXY6).
* Symmetry:
You can show [precalculated symmetry](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?bGH1BfLsiGFhhTDn8), or calculate [symmetry dynamically](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?6NvhQ45XrnbuXyGe6) using SymD.
* Use iCn3D in Jupyter Notebook:
You can use iCn3D in Jupyter Notebook with the widget "icn3dpy". The instructions are at [pypi.org/project/icn3dpy](https://pypi.org/project/icn3dpy/).
* 2D Cartoons in the chain, domain, and secondary structure levels:
You can use click "Analysis > 2D Cartoon" to show 2D Cartoons in the [chain](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?pzmT7EMTAxXKVbZu7), [domain](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?Arh4H9VTMuHQURY5A), and [secondary structure](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?5iZSHNbXcJisp7gQ6) levels.
* Contact Map for any Selected Residues:
You can click the menu "Analysis > Contact Map" to show the interactive [contact map](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?rnMbe26tNsAjJLGK9) for any selected residues. You can export the map in PNG or SVG.
* More features are listed at [www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html): [binding site](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?JR5B), [interaction interface](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?CuXYgGLCukDeUKnJ6), [3D printing](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbid=1tup&command=export+stl+stabilizer+file), [transmembrane proteins](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?TuSd), [surface](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?aYAjP4S3NbrBJX3x6), [EM map](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?L4C4WYE85tYRiFeK7), [electron density map from MTZ, CCP4, or DSN6](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?QpqNZ3k65ToYFvUB6), 1D sequences and 2D interactions, [align two structures](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?PfsQFtZRTgFAW2LG6), [align multiple chains](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?ijnf), [align a protein sequence to a structure](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?Mmm82craCwGMAxru9), [realign](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?UccFrXLDNeVB7Jk16), [custom tracks](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?pUzP), [force-directed graph for interactions](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?rshvjTFXpAFu8GDa9), [solvent accessible surface area](https://www.ncbi.nlm.nih.gov/Structure/icn3d/share.html?xKSyfd1umbKstGh29), save videos, etc.
## Embed iCn3D with iframe or JavaScript libraries
iCn3D can be embedded in a web page by including the URL in HTML iframe, e.g. . This method always shows the most recent version of iCn3D.
To embed iCn3D with JavaScript libraries, the following libraries need to be included: jQuery, jQuery UI, Three.js, and iCn3D library. An html div tag to hold the 3D viewer is added. The iCn3D widget is initialized with the custom defined parameter "cfg": "let icn3dui = new icn3d.iCn3DUI(cfg); await icn3dui.show3DStructure();". Multiple iCn3D widgets can be embedded in a single page. Please see the source code of the [example page](https://www.ncbi.nlm.nih.gov/Structure/icn3d/example-simple.html) for reference.
Users can choose to show the most recent version of iCn3D, or a locked version of iCn3D. To show the most recent version, use the library files without the version postfix as shown in the [iCn3D Doc page](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html#HowToUse). To show a locked version, use the library files with the version postfix as shown in the source code of [iCn3D page](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbid=1tup). If the input is provided as an MMDB ID, both library files and backend cgis are versioned so that the 3D display will be stable.
## Data Sources
iCn3D accepts the following IDs:
* mmdbafid: A list of PDB or AlphaFold UniProt IDs for realignment, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbafid=1HHO,4N7N,P69905,P01942](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbafid=1HHO,4N7N,P69905,P01942). You can then input multiple PDB files with the menu "File > Open File > PDB Files (appendable)". Next you can click the menu "File > Realign Selection > by Structure Alignment" to realign all loaded structures.
* protein: Protein/Gene name to search AlphaFold structures, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?protein=TP53&showanno=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?protein=TP53&showanno=1&showsets=1)
* mmdbid: NCBI MMDB ID, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbid=1tup&showanno=1&show2d=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmdbid=1tup&showanno=1&show2d=1&showsets=1)
* bcifid or mmtfid: Binary CIF ID or MMTF ID, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?bcifid=1tup&show2d=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?bcifid=1tup&showanno=1&showsets=1)
* pdbid: PDB ID, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?pdbid=1tup&showanno=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?pdbid=1tup&showanno=1&showsets=1)
* afid: AlphaFold Structure with UniProt ID, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?afid=A0A061AD48&showanno=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?afid=A0A061AD48&showanno=1&showsets=1)
* refseqid: AlphaFold Structure with NCBI Protein Accession, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?refseqid=NP_001743.1&show2d=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?refseqid=NP_001743.1&show2d=1&showsets=1)
* opmid: Orientations of Proteins in Membranes(OPM) PDB ID, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?opmid=6jxr&show2d=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?opmid=6jxr&showanno=1&showsets=1)
* mmcifid: mmCIF ID, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmcifid=1tup&show2d=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?mmcifid=1tup&showanno=1&showsets=1)
* cid: PubChem Compound ID, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?cid=2244](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?cid=2244)
* align two structures: two PDB IDs or MMDB IDs for structure alignment, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?align=1hho,4n7n&showalignseq=1&show2d=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?align=1hho,4n7n&showalignseq=1&show2d=1&showsets=1)
* align multiple chains: any multiple chains for structure alignment, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?chainalign=1HHO_A,4N7N_A&showalignseq=1&show2d=1&showsets=1](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?chainalign=1HHO_A,4N7N_A&showalignseq=1&show2d=1&showsets=1)
* blast_rep_id and query_id: NCBI protein accessions of a protein sequence and a chain of a 3D structure for sequence-structure alignment, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?from=icn3d&blast_rep_id=1TSR_A&query_id=NP_001108451.1&command=view annotations; set annotation cdd; set annotation site; set view detailed view; select chain 1TSR_A; show selection](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?from=icn3d&blast_rep_id=1TSR_A&query_id=NP_001108451.1&command=view annotations; set annotation cdd; set annotation site; set view detailed view; select chain 1TSR_A; show selection)
iCn3D also accepts the following file types: PDB, mmCIF, Mol2, SDF, XYZ, and iCn3D PNG. The files can be passed through a url, e.g., [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=pdb&url=https://storage.googleapis.com/membranome-assets/pdb_files/proteins/FCG2A_HUMAN.pdb](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=pdb&url=https://storage.googleapis.com/membranome-assets/pdb_files/proteins/FCG2A_HUMAN.pdb), [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=mmcif&url=https://files.rcsb.org/download/1GPK.cif](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=mmcif&url=https://files.rcsb.org/download/1GPK.cif), or [https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://api.figshare.com/v2/file/download/39125801](https://www.ncbi.nlm.nih.gov/Structure/icn3d/?type=icn3dpng&url=https://api.figshare.com/v2/file/download/39125801). See the [help page](https://www.ncbi.nlm.nih.gov/Structure/icn3d/docs/icn3d_help.html) or the [Doc page](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d.html) for more details.
## Third-party libraries used in Frontend
* **[jQuery and jQuery UI](https://jquery.com/)**: used as a general tool to write Javascript code. Some jQuery UI features are used.
* **[Three.js](http://threejs.org/)**: used to set up the 3D view.
* **[Force-Directed Graph](https://gist.github.com/pkerpedjiev/f2e6ebb2532dae603de13f0606563f5b)**: "2D Graph (Force-Directed)" in the menu "Analysis > Interactions" is based on Force-Directed Graph.
## Third-party libraries used in Backend
* **[DelPhi](http://honig.c2b2.columbia.edu/delphi)**: used to calculate electrostatic potential dynamically and is licensed from Columbia University.
* **[DelPhiPKa](http://compbio.clemson.edu/pka_webserver)**: used to add hydrogens and partial charges to proteins and nucleotides.
* **[Open Babel](http://openbabel.org/wiki/Main_Page)**: used to add hydrogens to ligands, convert PDB to SVG, and convert SMILES to PDB.
* **[Antechamber](http://ambermd.org/antechamber/ac.html)**: used to add partial charges to ligands.
* **[SymD](https://symd.nci.nih.gov/)**: used to calculate symmetry dynamically.
* **[scap/Jackal](http://honig.c2b2.columbia.edu/scap)**: used to predict side chain conformation dynamically.
* **[TM-align](https://zhanggroup.org/TM-align/)**: used to align two chains of 3D structures.
## Tools based on
* **[iview](https://bmcbioinformatics.biomedcentral.com/articles/10.1186/1471-2105-15-56)**: The drawing of 3D objects is based on iview.
* **[GLmol](https://webglmol.osdn.jp/index-en.html)**: The drawing of nucleotides cartoon is based on GLmol.
* **[3Dmol](https://3dmol.csb.pitt.edu/)**: The surface generation and labeling are based on 3Dmol.
* **[NGL Viewer](https://github.com/arose/ngl)**: The Imposter shaders are based on NGL Viewer.
* **[Mol\*](https://github.com/molstar/molstar)**: The parsers of MD trajectory files (DCD and XTC formats) and EM density data from PDBe are based on Mol\*.
* **[py3Dmol](https://pypi.org/project/py3Dmol/)**: The Jupyter Notebook widget "icn3dpy" is based on py3Dmol.
* **[Orientations of Proteins in Membranes (OPM)](https://opm.phar.umich.edu/)**: The membrane data of transmembrane proteins are from OPM.
* **[Membranome](https://membranome.org)**: For AlphaFold Structures, the membrane data of single-spanning transmembrane proteins are from Membranome.
* **[Post-Translational Modification (PTM)](https://www.ebi.ac.uk/proteins/api/doc/#/features)**: The PTM data are from UniProt.
* **[UglyMol](https://github.com/uglymol/uglymol.github.io)**: The electron density maps from CCP4 map or MTZ format are based on UglyMol.
## Building
If you want to build your code easily, you'll need to install nodejs and npm.
Next, clone this repository, and then perform the following setup steps in your working copy of icn3d.
```
npm config set -g production false
npm install -g gulp
npm install
npm install uglify-js@3.3.9
delete package-lock.json
```
The first line sets the npm default as dev so that all modules will be installed. The second line installs the gulp build tool globally, making the `gulp` command available on the command line. The third line install all modules. The fourth line changes the version of uglify-js to an old version, which does not compress class names. The last line may be required for a fresh build to remove old package-lock.json.
You only have to perform the above steps once, to set up your working directory. From then on, to build, simply enter:
```
gulp
```
## Contact
Please send all comments to wangjiy@ncbi.nlm.nih.gov.
## Citing
Wang J, Youkharibache P, Zhang D, Lanczycki CJ, Geer RC, Madej T, Phan L, Ward M, Lu S, Marchler GH, Wang Y, Bryant SH, Geer LY, Marchler-Bauer A. *iCn3D, a Web-based 3D Viewer for Sharing 1D/2D/3D Representations of Biomolecular Structures.* **_Bioinformatics_. 2020** Jan 1; 36(1):131-135. (Epub 2019 June 20.) [doi: 10.1093/bioinformatics/btz502](https://dx.doi.org/10.1093/bioinformatics/btz502). [PubMed PMID: 31218344](https://www.ncbi.nlm.nih.gov/pubmed/31218344), [Full Text at Oxford Academic](https://academic.oup.com/bioinformatics/article/36/1/131/5520951)
Wang J, Youkharibache P, Marchler-Bauer A, Lanczycki C, Zhang D, Lu S, Madej T, Marchler GH, Cheng T, Chong LC, Zhao S, Yang K, Lin J, Cheng Z, Dunn R, Malkaram SA, Tai C-H, Enoma D, Busby B, Johnson NL, Tabaro F, Song G, Ge Y. *iCn3D: From Web-Based 3D Viewer to Structural Analysis Tool in Batch Mode.* **_Front. Mol. Biosci._ 2022** 9:831740. (Epub 2022 Feb 17.) [doi: 10.3389/fmolb.2022.831740](https://dx.doi.org/10.3389/fmolb.2022.831740). [PubMed PMID: 35252351](https://www.ncbi.nlm.nih.gov/pubmed/35252351), [Full Text at Frontiers](https://www.frontiersin.org/articles/10.3389/fmolb.2022.831740/full)
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 3.0.x | :white_check_mark: |
| < 3.0 | :x: |
## Reporting a Vulnerability
Please send an email to wangjiy@ncbi.nlm.nih.gov to report a vulnerability.
================================================
FILE: build/icn3d.js
================================================
var $NGL_shaderTextHash = {};
$NGL_shaderTextHash['SphereImpostor.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_begin",
" #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;",
" }",
" 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",
"",
" cameraNormal = normalize( cameraPos - cameraSpherePos );",
" cameraNormal *= float(!interior) * 2.0 - 1.0;",
" return !interior;",
"",
"}",
"",
"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",
"",
" // don't use include normal_fragment",
" vec3 normal = normalize( vNormal );",
"",
" #include lights_physical_fragment",
" //include lights_template",
" #include lights_fragment_begin",
" #include lights_fragment_end",
"",
" 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",
" #ifdef USE_FOG",
" #ifdef USE_LOGDEPTHBUF_EXT",
" float depth = gl_FragDepthEXT / gl_FragCoord.w;",
" #else",
" float depth = gl_FragCoord.z / gl_FragCoord.w;",
" #endif",
" #ifdef FOG_EXP2",
" float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );",
" #else",
" float fogFactor = smoothstep( fogNear, fogFar, depth );",
" #endif",
" gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );",
" #endif",
"",
" #endif",
"",
"}"
].join("\n");
$NGL_shaderTextHash['SphereImpostor.vert'] = ["uniform mat4 projectionMatrixInverse;",
"uniform float nearClip;",
"",
"varying float vRadius;",
"varying float vRadiusSq;",
"varying vec3 vPoint;",
"varying vec3 vPointViewPosition;",
"varying float fogDepth;",
"varying float fogNear;",
"varying float fogFar;",
"",
"attribute vec2 mapping;",
"//attribute vec3 position;",
"attribute float radius;",
"",
"#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 transposeTmp( 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(){",
"",
" 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,",
" position.x, position.y, position.z, 1.0",
" );",
"",
" mat4 R = transposeTmp( 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;",
"",
"}",
"",
"void main(void){",
"",
" #ifdef PICKING",
" vPickingColor = unpackColor( primitiveId );",
" #else",
" #include color_vertex",
" #endif",
"",
" vRadius = radius * matrixScale( modelViewMatrix );",
"",
" vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
" // avoid clipping, added again in fragment shader",
" mvPosition.z -= vRadius;",
"",
" gl_Position = projectionMatrix * vec4( mvPosition.xyz, 1.0 );",
" ComputePointSizeAndPositionInClipCoordSphere();",
"",
"",
" vRadiusSq = vRadius * vRadius;",
" vec4 vPoint4 = projectionMatrixInverse * gl_Position;",
" vPoint = vPoint4.xyz / vPoint4.w;",
" vPointViewPosition = -mvPosition.xyz / mvPosition.w;",
"",
"}"
].join("\n");
$NGL_shaderTextHash['CylinderImpostor.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_begin",
" #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 );",
"",
" #include lights_physical_fragment",
" //include lights_template",
" #include lights_fragment_begin",
" #include lights_fragment_end",
"",
" 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",
" #ifdef USE_FOG",
" #ifdef USE_LOGDEPTHBUF_EXT",
" float depth = gl_FragDepthEXT / gl_FragCoord.w;",
" #else",
" float depth = gl_FragCoord.z / gl_FragCoord.w;",
" #endif",
" #ifdef FOG_EXP2",
" float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );",
" #else",
" float fogFactor = smoothstep( fogNear, fogFar, depth );",
" #endif",
" gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );",
" #endif",
"",
" #endif",
"",
"}"
].join("\n");
$NGL_shaderTextHash['CylinderImpostor.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;",
"",
"varying vec3 axis;",
"varying vec4 base_radius;",
"varying vec4 end_b;",
"varying vec3 U;",
"varying vec3 V;",
"varying vec4 w;",
"varying float fogDepth;",
"varying float fogNear;",
"varying float fogFar;",
"",
"#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 ] );",
"}",
"",
"void main(){",
"",
" #ifdef PICKING",
" vPickingColor = unpackColor( primitiveId );",
" #else",
" vColor1 = color;",
" vColor2 = color2;",
" #endif",
"",
" // vRadius = radius;",
" base_radius.w = radius * matrixScale( modelViewMatrix );",
"",
" //vec3 center = position;",
" vec3 center = ( position2 + position1 ) / 2.0;",
" vec3 dir = normalize( position2 - position1 );",
" 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");
$NGL_shaderTextHash['SphereInstancing.frag'] = $NGL_shaderTextHash['SphereImpostor.frag'];
$NGL_shaderTextHash['SphereInstancing.vert'] = ["uniform mat4 projectionMatrixInverse;",
"uniform float nearClip;",
"",
"varying float vRadius;",
"varying float vRadiusSq;",
"varying vec3 vPoint;",
"varying vec3 vPointViewPosition;",
"varying float fogDepth;",
"varying float fogNear;",
"varying float fogFar;",
"",
"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 transposeTmp( 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 = transposeTmp( 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");
$NGL_shaderTextHash['CylinderInstancing.frag'] = $NGL_shaderTextHash['CylinderImpostor.frag'];
$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;",
"varying float fogDepth;",
"varying float fogNear;",
"varying float fogFar;",
"",
"#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");
$NGL_shaderTextHash['Instancing.frag'] = ["#define STANDARD",
"uniform vec3 diffuse;",
"uniform vec3 emissive;",
"uniform float roughness;",
"uniform float metalness;",
"uniform float opacity;",
"uniform float nearClip;",
"uniform float clipRadius;",
"uniform mat4 projectionMatrix;",
"uniform float ortho;",
"varying float bCylinder;",
"",
"#if defined( NEAR_CLIP ) || defined( RADIUS_CLIP ) || ( !defined( PICKING ) && !defined( NOLIGHT ) )",
" varying vec3 vViewPosition;",
"#endif",
"",
"#if defined( RADIUS_CLIP )",
" varying vec3 vClipCenter;",
"#endif",
"",
"#if defined( PICKING )",
" uniform float objectId;",
" varying vec3 vPickingColor;",
"#elif defined( NOLIGHT )",
" varying vec3 vColor;",
"#else",
" #ifndef FLAT_SHADED",
" varying vec3 vNormal;",
" #endif",
" #include common",
" #include color_pars_fragment",
" #include fog_pars_fragment",
" #include bsdfs",
" #include lights_pars_begin",
" #include lights_physical_pars_fragment",
"#endif",
"",
"void main(){",
" #include nearclip_fragment",
" #include radiusclip_fragment",
"",
" #if defined( PICKING )",
"",
" gl_FragColor = vec4( vPickingColor, objectId );",
"",
" #elif defined( NOLIGHT )",
"",
" gl_FragColor = vec4( vColor, opacity );",
"",
" #else",
"",
" 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_begin",
"",
" //include dull_interior_fragment",
"",
" #include lights_physical_fragment",
" //include lights_template",
" #include lights_fragment_begin",
" #include lights_fragment_end",
"",
" vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight;",
"",
" #include interior_fragment",
"",
" gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
"",
" #include premultiplied_alpha_fragment",
" #include tonemapping_fragment",
" #include encodings_fragment",
" #include fog_fragment",
"",
" #include opaque_back_fragment",
"",
" #endif",
"",
"}"
].join("\n");
$NGL_shaderTextHash['Instancing.vert'] = ["#define STANDARD",
"",
"uniform mat4 projectionMatrixInverse;",
"uniform float nearClip;",
"uniform vec3 clipCenter;",
"attribute vec4 matrix1;",
"attribute vec4 matrix2;",
"attribute vec4 matrix3;",
"attribute vec4 matrix4;",
"attribute float cylinder;",
"varying float bCylinder;",
"",
"#if defined( NEAR_CLIP ) || defined( RADIUS_CLIP ) || ( !defined( PICKING ) && !defined( NOLIGHT ) )",
" varying vec3 vViewPosition;",
"#endif",
"",
"#if defined( RADIUS_CLIP )",
" varying vec3 vClipCenter;",
"#endif",
"",
"#if defined( PICKING )",
" #include unpack_color",
" attribute float primitiveId;",
" varying vec3 vPickingColor;",
"#elif defined( NOLIGHT )",
" varying vec3 vColor;",
"#else",
" #include color_pars_vertex",
" #ifndef FLAT_SHADED",
" varying vec3 vNormal;",
" #endif",
"#endif",
"",
"#include common",
"",
" 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(){",
" bCylinder = cylinder;",
"",
" mat4 matrix = computeMat(matrix1, matrix2, matrix3, matrix4);",
" vec4 updatePosition = matrix * vec4(position, 1.0);",
"",
" #if defined( PICKING )",
" vPickingColor = unpackColor( primitiveId );",
" #elif defined( NOLIGHT )",
" vColor = color;",
" #else",
" #include color_vertex",
" //include beginnormal_vertex",
" //vec3 objectNormal = vec3( normal );",
" vec3 objectNormal = vec3(matrix * vec4(normal,0.0));",
" #include defaultnormal_vertex",
" // Normal computed with derivatives when FLAT_SHADED",
" #ifndef FLAT_SHADED",
" vNormal = normalize( transformedNormal );",
" #endif",
" #endif",
"",
" //include begin_vertex",
" vec3 transformed = updatePosition.xyz;",
" //include project_vertex",
" vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );",
" gl_Position = projectionMatrix * mvPosition;",
"",
" #if defined( NEAR_CLIP ) || defined( RADIUS_CLIP ) || ( !defined( PICKING ) && !defined( NOLIGHT ) )",
" vViewPosition = -mvPosition.xyz;",
" #endif",
"",
" #if defined( RADIUS_CLIP )",
" vClipCenter = -( modelViewMatrix * vec4( clipCenter, 1.0 ) ).xyz;",
" #endif",
"",
" #include nearclip_vertex",
"",
"}"
].join("\n");
// ; var __CIFTools = function () {
// 'use strict';
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
var CIFTools;
(function (CIFTools) {
CIFTools.VERSION = { number: "1.1.7", date: "Oct 30 2018" };
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
var Utils;
(function (Utils) {
var ChunkedArray;
(function (ChunkedArray) {
function is(x) {
return x.creator && x.chunkSize;
}
ChunkedArray.is = is;
function add4(array, x, y, z, w) {
if (array.currentIndex >= array.chunkSize) {
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
}
array.current[array.currentIndex++] = x;
array.current[array.currentIndex++] = y;
array.current[array.currentIndex++] = z;
array.current[array.currentIndex++] = w;
return array.elementCount++;
}
ChunkedArray.add4 = add4;
function add3(array, x, y, z) {
if (array.currentIndex >= array.chunkSize) {
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
}
array.current[array.currentIndex++] = x;
array.current[array.currentIndex++] = y;
array.current[array.currentIndex++] = z;
return array.elementCount++;
}
ChunkedArray.add3 = add3;
function add2(array, x, y) {
if (array.currentIndex >= array.chunkSize) {
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
}
array.current[array.currentIndex++] = x;
array.current[array.currentIndex++] = y;
return array.elementCount++;
}
ChunkedArray.add2 = add2;
function add(array, x) {
if (array.currentIndex >= array.chunkSize) {
array.currentIndex = 0;
array.current = array.creator(array.chunkSize);
array.parts[array.parts.length] = array.current;
}
array.current[array.currentIndex++] = x;
return array.elementCount++;
}
ChunkedArray.add = add;
function compact(array) {
var ret = array.creator(array.elementSize * array.elementCount), offset = (array.parts.length - 1) * array.chunkSize, offsetInner = 0, part;
if (array.parts.length > 1) {
if (array.parts[0].buffer) {
for (var i = 0; i < array.parts.length - 1; i++) {
ret.set(array.parts[i], array.chunkSize * i);
}
}
else {
for (var i = 0; i < array.parts.length - 1; i++) {
offsetInner = array.chunkSize * i;
part = array.parts[i];
for (var j = 0; j < array.chunkSize; j++) {
ret[offsetInner + j] = part[j];
}
}
}
}
if (array.current.buffer && array.currentIndex >= array.chunkSize) {
ret.set(array.current, array.chunkSize * (array.parts.length - 1));
}
else {
for (var i = 0; i < array.currentIndex; i++) {
ret[offset + i] = array.current[i];
}
}
return ret;
}
ChunkedArray.compact = compact;
function forVertex3D(chunkVertexCount) {
if (chunkVertexCount === void 0) { chunkVertexCount = 262144; }
return create(function (size) { return new Float32Array(size); }, chunkVertexCount, 3);
}
ChunkedArray.forVertex3D = forVertex3D;
function forIndexBuffer(chunkIndexCount) {
if (chunkIndexCount === void 0) { chunkIndexCount = 262144; }
return create(function (size) { return new Uint32Array(size); }, chunkIndexCount, 3);
}
ChunkedArray.forIndexBuffer = forIndexBuffer;
function forTokenIndices(chunkTokenCount) {
if (chunkTokenCount === void 0) { chunkTokenCount = 131072; }
return create(function (size) { return new Int32Array(size); }, chunkTokenCount, 2);
}
ChunkedArray.forTokenIndices = forTokenIndices;
function forIndices(chunkTokenCount) {
if (chunkTokenCount === void 0) { chunkTokenCount = 131072; }
return create(function (size) { return new Int32Array(size); }, chunkTokenCount, 1);
}
ChunkedArray.forIndices = forIndices;
function forInt32(chunkSize) {
if (chunkSize === void 0) { chunkSize = 131072; }
return create(function (size) { return new Int32Array(size); }, chunkSize, 1);
}
ChunkedArray.forInt32 = forInt32;
function forFloat32(chunkSize) {
if (chunkSize === void 0) { chunkSize = 131072; }
return create(function (size) { return new Float32Array(size); }, chunkSize, 1);
}
ChunkedArray.forFloat32 = forFloat32;
function forArray(chunkSize) {
if (chunkSize === void 0) { chunkSize = 131072; }
return create(function (size) { return []; }, chunkSize, 1);
}
ChunkedArray.forArray = forArray;
function create(creator, chunkElementCount, elementSize) {
chunkElementCount = chunkElementCount | 0;
if (chunkElementCount <= 0)
chunkElementCount = 1;
var chunkSize = chunkElementCount * elementSize;
var current = creator(chunkSize);
return {
elementSize: elementSize,
chunkSize: chunkSize,
creator: creator,
current: current,
parts: [current],
currentIndex: 0,
elementCount: 0
};
}
ChunkedArray.create = create;
})(ChunkedArray = Utils.ChunkedArray || (Utils.ChunkedArray = {}));
})(Utils = CIFTools.Utils || (CIFTools.Utils = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
/**
* Efficient integer and float parsers.
*
* For the purposes of parsing numbers from the mmCIF data representations,
* up to 4 times faster than JS parseInt/parseFloat.
*/
// var CIFTools;
// (function (CIFTools) {
// var Utils;
(function (Utils) {
var FastNumberParsers;
(function (FastNumberParsers) {
"use strict";
function parseIntSkipTrailingWhitespace(str, start, end) {
while (start < end && str.charCodeAt(start) === 32)
start++;
return parseInt(str, start, end);
}
FastNumberParsers.parseIntSkipTrailingWhitespace = parseIntSkipTrailingWhitespace;
function parseInt(str, start, end) {
var ret = 0, neg = 1;
if (str.charCodeAt(start) === 45 /* - */) {
neg = -1;
start++;
}
for (; start < end; start++) {
var c = str.charCodeAt(start) - 48;
if (c > 9 || c < 0)
return (neg * ret) | 0;
else
ret = (10 * ret + c) | 0;
}
return neg * ret;
}
FastNumberParsers.parseInt = parseInt;
function parseScientific(main, str, start, end) {
// handle + in '1e+1' separately.
if (str.charCodeAt(start) === 43 /* + */)
start++;
return main * Math.pow(10.0, parseInt(str, start, end));
}
function parseFloatSkipTrailingWhitespace(str, start, end) {
while (start < end && str.charCodeAt(start) === 32)
start++;
return parseFloat(str, start, end);
}
FastNumberParsers.parseFloatSkipTrailingWhitespace = parseFloatSkipTrailingWhitespace;
function parseFloat(str, start, end) {
var neg = 1.0, ret = 0.0, point = 0.0, div = 1.0;
if (str.charCodeAt(start) === 45) {
neg = -1.0;
++start;
}
while (start < end) {
var c = str.charCodeAt(start) - 48;
if (c >= 0 && c < 10) {
ret = ret * 10 + c;
++start;
}
else if (c === -2) { // .
++start;
while (start < end) {
c = str.charCodeAt(start) - 48;
if (c >= 0 && c < 10) {
point = 10.0 * point + c;
div = 10.0 * div;
++start;
}
else if (c === 53 || c === 21) { // 'e'/'E'
return parseScientific(neg * (ret + point / div), str, start + 1, end);
}
else {
return neg * (ret + point / div);
}
}
return neg * (ret + point / div);
}
else if (c === 53 || c === 21) { // 'e'/'E'
return parseScientific(neg * ret, str, start + 1, end);
}
else
break;
}
return neg * ret;
}
FastNumberParsers.parseFloat = parseFloat;
})(FastNumberParsers = Utils.FastNumberParsers || (Utils.FastNumberParsers = {}));
})(Utils = CIFTools.Utils || (CIFTools.Utils = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Utils;
(function (Utils) {
var __paddingSpaces = [];
(function () {
var s = '';
for (var i = 0; i < 512; i++) {
__paddingSpaces[i] = s;
s = s + ' ';
}
})();
var StringWriter;
(function (StringWriter) {
function create(chunkCapacity) {
if (chunkCapacity === void 0) { chunkCapacity = 512; }
return {
chunkData: [],
chunkOffset: 0,
chunkCapacity: chunkCapacity,
data: []
};
}
StringWriter.create = create;
function asString(writer) {
if (!writer.data.length) {
if (writer.chunkData.length === writer.chunkOffset)
return writer.chunkData.join('');
return writer.chunkData.splice(0, writer.chunkOffset).join('');
}
if (writer.chunkOffset > 0) {
writer.data[writer.data.length] = writer.chunkData.splice(0, writer.chunkOffset).join('');
}
return writer.data.join('');
}
StringWriter.asString = asString;
function writeTo(writer, stream) {
finalize(writer);
for (var _i = 0, _a = writer.data; _i < _a.length; _i++) {
var s = _a[_i];
stream.writeString(s);
}
}
StringWriter.writeTo = writeTo;
function finalize(writer) {
if (writer.chunkOffset > 0) {
if (writer.chunkData.length === writer.chunkOffset)
writer.data[writer.data.length] = writer.chunkData.join('');
else
writer.data[writer.data.length] = writer.chunkData.splice(0, writer.chunkOffset).join('');
writer.chunkOffset = 0;
}
}
function newline(writer) {
write(writer, '\n');
}
StringWriter.newline = newline;
function whitespace(writer, len) {
write(writer, __paddingSpaces[len]);
}
StringWriter.whitespace = whitespace;
function write(writer, val) {
if (val === undefined || val === null) {
return;
}
if (writer.chunkOffset === writer.chunkCapacity) {
writer.data[writer.data.length] = writer.chunkData.join('');
writer.chunkOffset = 0;
}
writer.chunkData[writer.chunkOffset++] = val;
}
StringWriter.write = write;
function writeSafe(writer, val) {
if (writer.chunkOffset === writer.chunkCapacity) {
writer.data[writer.data.length] = writer.chunkData.join('');
writer.chunkOffset = 0;
}
writer.chunkData[writer.chunkOffset++] = val;
}
StringWriter.writeSafe = writeSafe;
function writePadLeft(writer, val, totalWidth) {
if (val === undefined || val === null) {
write(writer, __paddingSpaces[totalWidth]);
}
var padding = totalWidth - val.length;
if (padding > 0)
write(writer, __paddingSpaces[padding]);
write(writer, val);
}
StringWriter.writePadLeft = writePadLeft;
function writePadRight(writer, val, totalWidth) {
if (val === undefined || val === null) {
write(writer, __paddingSpaces[totalWidth]);
}
var padding = totalWidth - val.length;
write(writer, val);
if (padding > 0)
write(writer, __paddingSpaces[padding]);
}
StringWriter.writePadRight = writePadRight;
function writeInteger(writer, val) {
write(writer, '' + val);
}
StringWriter.writeInteger = writeInteger;
function writeIntegerPadLeft(writer, val, totalWidth) {
var s = '' + val;
var padding = totalWidth - s.length;
if (padding > 0)
write(writer, __paddingSpaces[padding]);
write(writer, s);
}
StringWriter.writeIntegerPadLeft = writeIntegerPadLeft;
function writeIntegerPadRight(writer, val, totalWidth) {
var s = '' + val;
var padding = totalWidth - s.length;
write(writer, s);
if (padding > 0)
write(writer, __paddingSpaces[padding]);
}
StringWriter.writeIntegerPadRight = writeIntegerPadRight;
/**
* @example writeFloat(123.2123, 100) -- 2 decim
*/
function writeFloat(writer, val, precisionMultiplier) {
write(writer, '' + Math.round(precisionMultiplier * val) / precisionMultiplier);
}
StringWriter.writeFloat = writeFloat;
function writeFloatPadLeft(writer, val, precisionMultiplier, totalWidth) {
var s = '' + Math.round(precisionMultiplier * val) / precisionMultiplier;
var padding = totalWidth - s.length;
if (padding > 0)
write(writer, __paddingSpaces[padding]);
write(writer, s);
}
StringWriter.writeFloatPadLeft = writeFloatPadLeft;
function writeFloatPadRight(writer, val, precisionMultiplier, totalWidth) {
var s = '' + Math.round(precisionMultiplier * val) / precisionMultiplier;
var padding = totalWidth - s.length;
write(writer, s);
if (padding > 0)
write(writer, __paddingSpaces[padding]);
}
StringWriter.writeFloatPadRight = writeFloatPadRight;
})(StringWriter = Utils.StringWriter || (Utils.StringWriter = {}));
})(Utils = CIFTools.Utils || (CIFTools.Utils = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// "use strict";
/**
* Represents a column that is not present.
*/
var _UndefinedColumn = /** @class */ (function () {
function _UndefinedColumn() {
this.isDefined = false;
}
_UndefinedColumn.prototype.getString = function (row) { return null; };
;
_UndefinedColumn.prototype.getInteger = function (row) { return 0; };
_UndefinedColumn.prototype.getFloat = function (row) { return 0.0; };
_UndefinedColumn.prototype.getValuePresence = function (row) { return 1 /* NotSpecified */; };
_UndefinedColumn.prototype.areValuesEqual = function (rowA, rowB) { return true; };
_UndefinedColumn.prototype.stringEquals = function (row, value) { return value === null; };
return _UndefinedColumn;
}());
CIFTools.UndefinedColumn = new _UndefinedColumn();
/**
* Helper functions for categoies.
*/
var Category;
(function (Category) {
/**
* Extracts a matrix from a category from a specified rowIndex.
*
* _category.matrix[1][1] v11
* ....
* ....
* _category.matrix[rows][cols] vRowsCols
*/
function getMatrix(category, field, rows, cols, rowIndex) {
var ret = [];
for (var i = 1; i <= rows; i++) {
var row = [];
for (var j = 1; j <= cols; j++) {
row[j - 1] = category.getColumn(field + "[" + i + "][" + j + "]").getFloat(rowIndex);
}
ret[i - 1] = row;
}
return ret;
}
Category.getMatrix = getMatrix;
/**
* Extracts a vector from a category from a specified rowIndex.
*
* _category.matrix[1][1] v11
* ....
* ....
* _category.matrix[rows][cols] vRowsCols
*/
function getVector(category, field, rows, cols, rowIndex) {
var ret = [];
for (var i = 1; i <= rows; i++) {
ret[i - 1] = category.getColumn(field + "[" + i + "]").getFloat(rowIndex);
}
return ret;
}
Category.getVector = getVector;
})(Category = CIFTools.Category || (CIFTools.Category = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// "use strict";
var ParserResult;
(function (ParserResult) {
function error(message, line) {
if (line === void 0) { line = -1; }
return new ParserError(message, line);
}
ParserResult.error = error;
function success(result, warnings) {
if (warnings === void 0) { warnings = []; }
return new ParserSuccess(result, warnings);
}
ParserResult.success = success;
})(ParserResult = CIFTools.ParserResult || (CIFTools.ParserResult = {}));
var ParserError = /** @class */ (function () {
function ParserError(message, line) {
this.message = message;
this.line = line;
this.isError = true;
}
ParserError.prototype.toString = function () {
if (this.line >= 0) {
return "[Line " + this.line + "] " + this.message;
}
return this.message;
};
return ParserError;
}());
CIFTools.ParserError = ParserError;
var ParserSuccess = /** @class */ (function () {
function ParserSuccess(result, warnings) {
this.result = result;
this.warnings = warnings;
this.isError = false;
}
return ParserSuccess;
}());
CIFTools.ParserSuccess = ParserSuccess;
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
/*
On data representation of molecular files
Consider an mmCIF file that stores a molecule with 100k atoms. For the sake of simplicity,
lets ignore things like symmetry or assemblies, and assume, that the file only stores the
_atom_site records. The atom site "table" in the standard mmCIF from PDB database currently
has 26 columns.
So the data looks something like this:
loop_
_atom_site.column1
....
_atom_site.column26
t1,1 .... t1,26
t100000,1 .... t100000,26
The straightforward way to represent this data in JavaScript is to have an array of objects
with properties named "column1" ..., "column26":
[{ column1: "t1,1", ..., column26: "t1,26" },
...,
{ column1: "t100000,1", ..., column26: "t100000,26" }]
So in order to represent the atoms sites, we would need 100k objects and 2.6 million strings.
Is this bad? well, sort of. It would not be so bad if this representation would be the only
thing we need to keep in memory and/or the life time of the object was short. But usually
we would need to keep the object around for the entire lifetime of the app. This alone
adds a very non-significant overhead for the garbage collector (which increases the app's
latency). What's worse is that we usually only need a fraction of this data, but this can
vary application for application. For just 100k atoms, the overhead is not "that bad", but
consider 1M atoms and suddenly we have a problem.
The following data model shows an alternative way of storing molecular file s
in memory that is very efficient, fast and introduces a very minimal overhead.
*/
// var CIFTools;
// (function (CIFTools) {
var Text;
(function (Text) {
"use strict";
var ShortStringPool;
(function (ShortStringPool) {
function create() { return Object.create(null); }
ShortStringPool.create = create;
function get(pool, str) {
if (str.length > 6)
return str;
var value = pool[str];
if (value !== void 0)
return value;
pool[str] = str;
return str;
}
ShortStringPool.get = get;
})(ShortStringPool || (ShortStringPool = {}));
/**
* Represents the input file.
*/
var File = /** @class */ (function () {
function File(data) {
/**
* Data blocks inside the file. If no data block is present, a "default" one is created.
*/
this.dataBlocks = [];
this.data = data;
}
File.prototype.toJSON = function () {
return this.dataBlocks.map(function (b) { return b.toJSON(); });
};
return File;
}());
Text.File = File;
/**
* Represents a single data block.
*/
var DataBlock = /** @class */ (function () {
function DataBlock(data, header) {
this.header = header;
this.data = data;
this.categoryList = [];
this.additionalData = {};
this.categoryMap = new Map();
}
Object.defineProperty(DataBlock.prototype, "categories", {
/**
* Categories of the block.
* block.categories._atom_site / ['_atom_site']
*/
get: function () {
return this.categoryList;
},
enumerable: true,
configurable: true
});
/**
* Gets a category by its name.
*/
DataBlock.prototype.getCategory = function (name) {
return this.categoryMap.get(name);
};
/**
* Adds a category.
*/
DataBlock.prototype.addCategory = function (category) {
this.categoryList[this.categoryList.length] = category;
this.categoryMap.set(category.name, category);
};
DataBlock.prototype.toJSON = function () {
return {
id: this.header,
categories: this.categoryList.map(function (c) { return c.toJSON(); }),
additionalData: this.additionalData
};
};
return DataBlock;
}());
Text.DataBlock = DataBlock;
/**
* Represents a single CIF category.
*/
var Category = /** @class */ (function () {
function Category(data, name, startIndex, endIndex, columns, tokens, tokenCount) {
this.name = name;
this.tokens = tokens;
this.data = data;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.columnCount = columns.length;
this.rowCount = (tokenCount / columns.length) | 0;
this.columnIndices = new Map();
this.columnNameList = [];
for (var i = 0; i < columns.length; i++) {
var colName = columns[i].substr(name.length + 1);
this.columnIndices.set(colName, i);
this.columnNameList.push(colName);
}
}
Object.defineProperty(Category.prototype, "columnNames", {
/**
* The array of columns.
*/
get: function () {
return this.columnNameList;
},
enumerable: true,
configurable: true
});
/**
* Get a column object that makes accessing data easier.
* @returns undefined if the column isn't present, the Column object otherwise.
*/
Category.prototype.getColumn = function (name) {
var i = this.columnIndices.get(name);
if (i !== void 0)
return new Column(this, this.data, name, i);
return CIFTools.UndefinedColumn;
};
Category.prototype.toJSON = function () {
var rows = [], data = this.data, tokens = this.tokens;
var colNames = this.columnNameList;
var strings = ShortStringPool.create();
for (var i = 0; i < this.rowCount; i++) {
var item = {};
for (var j = 0; j < this.columnCount; j++) {
var tk = (i * this.columnCount + j) * 2;
item[colNames[j]] = ShortStringPool.get(strings, data.substring(tokens[tk], tokens[tk + 1]));
}
rows[i] = item;
}
return { name: this.name, columns: colNames, rows: rows };
};
return Category;
}());
Text.Category = Category;
var fastParseInt = CIFTools.Utils.FastNumberParsers.parseInt;
var fastParseFloat = CIFTools.Utils.FastNumberParsers.parseFloat;
/**
* Represents a single column of a CIF category.
*/
var Column = /** @class */ (function () {
function Column(category, data, name, index) {
this.data = data;
this.name = name;
this.index = index;
this.stringPool = ShortStringPool.create();
this.isDefined = true;
this.tokens = category.tokens;
this.columnCount = category.columnCount;
}
/**
* Returns the string value at given row.
*/
Column.prototype.getString = function (row) {
var i = (row * this.columnCount + this.index) * 2;
var ret = ShortStringPool.get(this.stringPool, this.data.substring(this.tokens[i], this.tokens[i + 1]));
if (ret === "." || ret === "?")
return null;
return ret;
};
/**
* Returns the integer value at given row.
*/
Column.prototype.getInteger = function (row) {
var i = (row * this.columnCount + this.index) * 2;
return fastParseInt(this.data, this.tokens[i], this.tokens[i + 1]);
};
/**
* Returns the float value at given row.
*/
Column.prototype.getFloat = function (row) {
var i = (row * this.columnCount + this.index) * 2;
return fastParseFloat(this.data, this.tokens[i], this.tokens[i + 1]);
};
/**
* Returns true if the token has the specified string value.
*/
Column.prototype.stringEquals = function (row, value) {
var aIndex = (row * this.columnCount + this.index) * 2, s = this.tokens[aIndex], len = value.length;
if (len !== this.tokens[aIndex + 1] - s)
return false;
for (var i = 0; i < len; i++) {
if (this.data.charCodeAt(i + s) !== value.charCodeAt(i))
return false;
}
return true;
};
/**
* Determines if values at the given rows are equal.
*/
Column.prototype.areValuesEqual = function (rowA, rowB) {
var aIndex = (rowA * this.columnCount + this.index) * 2, bIndex = (rowB * this.columnCount + this.index) * 2;
var aS = this.tokens[aIndex], bS = this.tokens[bIndex], len = this.tokens[aIndex + 1] - aS;
if (len !== this.tokens[bIndex + 1] - bS)
return false;
for (var i = 0; i < len; i++) {
if (this.data.charCodeAt(i + aS) !== this.data.charCodeAt(i + bS)) {
return false;
}
}
return true;
};
/**
* Returns true if the value is not defined (. or ? token).
*/
Column.prototype.getValuePresence = function (row) {
var index = row * this.columnCount + this.index;
var s = this.tokens[2 * index];
if (this.tokens[2 * index + 1] - s !== 1)
return 0 /* Present */;
var v = this.data.charCodeAt(s);
if (v === 46 /* . */)
return 1 /* NotSpecified */;
if (v === 63 /* ? */)
return 2 /* Unknown */;
return 0 /* Present */;
};
return Column;
}());
Text.Column = Column;
})(Text = CIFTools.Text || (CIFTools.Text = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Text;
(function (Text) {
"use strict";
var TokenIndexBuilder;
(function (TokenIndexBuilder) {
function resize(builder) {
// scale the size using golden ratio, because why not.
var newBuffer = new Int32Array((1.61 * builder.tokens.length) | 0);
newBuffer.set(builder.tokens);
builder.tokens = newBuffer;
builder.tokensLenMinus2 = (newBuffer.length - 2) | 0;
}
function addToken(builder, start, end) {
if (builder.count >= builder.tokensLenMinus2) {
resize(builder);
}
builder.tokens[builder.count++] = start;
builder.tokens[builder.count++] = end;
}
TokenIndexBuilder.addToken = addToken;
function create(size) {
return {
tokensLenMinus2: (size - 2) | 0,
count: 0,
tokens: new Int32Array(size)
};
}
TokenIndexBuilder.create = create;
})(TokenIndexBuilder || (TokenIndexBuilder = {}));
/**
* Eat everything until a whitespace/newline occurs.
*/
function eatValue(state) {
while (state.position < state.length) {
switch (state.data.charCodeAt(state.position)) {
case 9: // \t
case 10: // \n
case 13: // \r
case 32: // ' '
state.currentTokenEnd = state.position;
return;
default:
++state.position;
break;
}
}
state.currentTokenEnd = state.position;
}
/**
* Eats an escaped values. Handles the "degenerate" cases as well.
*
* "Degenerate" cases:
* - 'xx'x' => xx'x
* - 'xxxNEWLINE => 'xxx
*
*/
function eatEscaped(state, esc) {
var next, c;
++state.position;
while (state.position < state.length) {
c = state.data.charCodeAt(state.position);
if (c === esc) {
next = state.data.charCodeAt(state.position + 1);
switch (next) {
case 9: // \t
case 10: // \n
case 13: // \r
case 32: // ' '
// get rid of the quotes.
state.currentTokenStart++;
state.currentTokenEnd = state.position;
state.isEscaped = true;
++state.position;
return;
default:
if (next === void 0) { // = "end of stream"
// get rid of the quotes.
state.currentTokenStart++;
state.currentTokenEnd = state.position;
state.isEscaped = true;
++state.position;
return;
}
++state.position;
break;
}
}
else {
// handle 'xxxNEWLINE => 'xxx
if (c === 10 || c === 13) {
state.currentTokenEnd = state.position;
return;
}
++state.position;
}
}
state.currentTokenEnd = state.position;
}
/**
* Eats a multiline token of the form NL;....NL;
*/
function eatMultiline(state) {
var prev = 59, pos = state.position + 1, c;
while (pos < state.length) {
c = state.data.charCodeAt(pos);
if (c === 59 && (prev === 10 || prev === 13)) { // ;, \n \r
state.position = pos + 1;
// get rid of the ;
state.currentTokenStart++;
// remove trailing newlines
pos--;
c = state.data.charCodeAt(pos);
while (c === 10 || c === 13) {
pos--;
c = state.data.charCodeAt(pos);
}
state.currentTokenEnd = pos + 1;
state.isEscaped = true;
return;
}
else {
// handle line numbers
if (c === 13) { // \r
state.currentLineNumber++;
}
else if (c === 10 && prev !== 13) { // \r\n
state.currentLineNumber++;
}
prev = c;
++pos;
}
}
state.position = pos;
return prev;
}
/**
* Skips until \n or \r occurs -- therefore the newlines get handled by the "skipWhitespace" function.
*/
function skipCommentLine(state) {
while (state.position < state.length) {
var c = state.data.charCodeAt(state.position);
if (c === 10 || c === 13) {
return;
}
++state.position;
}
}
/**
* Skips all the whitespace - space, tab, newline, CR
* Handles incrementing line count.
*/
function skipWhitespace(state) {
var prev = 10;
while (state.position < state.length) {
var c = state.data.charCodeAt(state.position);
switch (c) {
case 9: // '\t'
case 32: // ' '
prev = c;
++state.position;
break;
case 10: // \n
// handle \r\n
if (prev !== 13) {
++state.currentLineNumber;
}
prev = c;
++state.position;
break;
case 13: // \r
prev = c;
++state.position;
++state.currentLineNumber;
break;
default:
return prev;
}
}
return prev;
}
function isData(state) {
// here we already assume the 5th char is _ and that the length >= 5
// d/D
var c = state.data.charCodeAt(state.currentTokenStart);
if (c !== 68 && c !== 100)
return false;
// a/A
c = state.data.charCodeAt(state.currentTokenStart + 1);
if (c !== 65 && c !== 97)
return false;
// t/t
c = state.data.charCodeAt(state.currentTokenStart + 2);
if (c !== 84 && c !== 116)
return false;
// a/A
c = state.data.charCodeAt(state.currentTokenStart + 3);
if (c !== 65 && c !== 97)
return false;
return true;
}
function isSave(state) {
// here we already assume the 5th char is _ and that the length >= 5
// s/S
var c = state.data.charCodeAt(state.currentTokenStart);
if (c !== 83 && c !== 115)
return false;
// a/A
c = state.data.charCodeAt(state.currentTokenStart + 1);
if (c !== 65 && c !== 97)
return false;
// v/V
c = state.data.charCodeAt(state.currentTokenStart + 2);
if (c !== 86 && c !== 118)
return false;
// e/E
c = state.data.charCodeAt(state.currentTokenStart + 3);
if (c !== 69 && c !== 101)
return false;
return true;
}
function isLoop(state) {
// here we already assume the 5th char is _ and that the length >= 5
if (state.currentTokenEnd - state.currentTokenStart !== 5)
return false;
// l/L
var c = state.data.charCodeAt(state.currentTokenStart);
if (c !== 76 && c !== 108)
return false;
// o/O
c = state.data.charCodeAt(state.currentTokenStart + 1);
if (c !== 79 && c !== 111)
return false;
// o/O
c = state.data.charCodeAt(state.currentTokenStart + 2);
if (c !== 79 && c !== 111)
return false;
// p/P
c = state.data.charCodeAt(state.currentTokenStart + 3);
if (c !== 80 && c !== 112)
return false;
return true;
}
/**
* Checks if the current token shares the namespace with string at = state.length) {
state.currentTokenType = 6 /* End */;
return;
}
state.currentTokenStart = state.position;
state.currentTokenEnd = state.position;
state.isEscaped = false;
var c = state.data.charCodeAt(state.position);
switch (c) {
case 35: // #, comment
skipCommentLine(state);
state.currentTokenType = 5 /* Comment */;
break;
case 34: // ", escaped value
case 39: // ', escaped value
eatEscaped(state, c);
state.currentTokenType = 3 /* Value */;
break;
case 59: // ;, possible multiline value
// multiline value must start at the beginning of the line.
if (prev === 10 || prev === 13) { // /n or /r
eatMultiline(state);
}
else {
eatValue(state);
}
state.currentTokenType = 3 /* Value */;
break;
default:
eatValue(state);
// escaped is always Value
if (state.isEscaped) {
state.currentTokenType = 3 /* Value */;
// _ always means column name
}
else if (state.data.charCodeAt(state.currentTokenStart) === 95) { // _
state.currentTokenType = 4 /* ColumnName */;
// 5th char needs to be _ for data_ or loop_
}
else if (state.currentTokenEnd - state.currentTokenStart >= 5 && state.data.charCodeAt(state.currentTokenStart + 4) === 95) {
if (isData(state))
state.currentTokenType = 0 /* Data */;
else if (isSave(state))
state.currentTokenType = 1 /* Save */;
else if (isLoop(state))
state.currentTokenType = 2 /* Loop */;
else
state.currentTokenType = 3 /* Value */;
// all other tests failed, we are at Value token.
}
else {
state.currentTokenType = 3 /* Value */;
}
break;
}
}
/**
* Moves to the next non-comment token.
*/
function moveNext(state) {
moveNextInternal(state);
while (state.currentTokenType === 5 /* Comment */)
moveNextInternal(state);
}
function createTokenizer(data) {
return {
data: data,
length: data.length,
position: 0,
currentTokenStart: 0,
currentTokenEnd: 0,
currentTokenType: 6 /* End */,
currentLineNumber: 1,
isEscaped: false
};
}
/**
* Reads a category containing a single row.
*/
function handleSingle(tokenizer, block) {
var nsStart = tokenizer.currentTokenStart, nsEnd = getNamespaceEnd(tokenizer), name = getNamespace(tokenizer, nsEnd), column, columns = [], tokens = TokenIndexBuilder.create(512), tokenCount = 0, readingNames = true;
while (readingNames) {
if (tokenizer.currentTokenType !== 4 /* ColumnName */ || !isNamespace(tokenizer, nsStart, nsEnd)) {
readingNames = false;
break;
}
column = getTokenString(tokenizer);
moveNext(tokenizer);
if (tokenizer.currentTokenType !== 3 /* Value */) {
return {
hasError: true,
errorLine: tokenizer.currentLineNumber,
errorMessage: "Expected value."
};
}
columns[columns.length] = column;
TokenIndexBuilder.addToken(tokens, tokenizer.currentTokenStart, tokenizer.currentTokenEnd);
tokenCount++;
moveNext(tokenizer);
}
block.addCategory(new Text.Category(block.data, name, nsStart, tokenizer.currentTokenStart, columns, tokens.tokens, tokenCount));
return {
hasError: false,
errorLine: 0,
errorMessage: ""
};
}
/**
* Reads a loop.
*/
function handleLoop(tokenizer, block) {
var start = tokenizer.currentTokenStart, loopLine = tokenizer.currentLineNumber;
moveNext(tokenizer);
var name = getNamespace(tokenizer, getNamespaceEnd(tokenizer)), columns = [], tokens = TokenIndexBuilder.create(name === "_atom_site" ? (block.data.length / 1.85) | 0 : 1024), tokenCount = 0;
while (tokenizer.currentTokenType === 4 /* ColumnName */) {
columns[columns.length] = getTokenString(tokenizer);
moveNext(tokenizer);
}
while (tokenizer.currentTokenType === 3 /* Value */) {
TokenIndexBuilder.addToken(tokens, tokenizer.currentTokenStart, tokenizer.currentTokenEnd);
tokenCount++;
moveNext(tokenizer);
}
if (tokenCount % columns.length !== 0) {
return {
hasError: true,
errorLine: tokenizer.currentLineNumber,
errorMessage: "The number of values for loop starting at line " + loopLine + " is not a multiple of the number of columns."
};
}
block.addCategory(new Text.Category(block.data, name, start, tokenizer.currentTokenStart, columns, tokens.tokens, tokenCount));
return {
hasError: false,
errorLine: 0,
errorMessage: ""
};
}
/**
* Creates an error result.
*/
function error(line, message) {
return CIFTools.ParserResult.error(message, line);
}
/**
* Creates a data result.
*/
function result(data) {
return CIFTools.ParserResult.success(data);
}
/**
* Parses an mmCIF file.
*
* @returns CifParserResult wrapper of the result.
*/
function parseInternal(data) {
var tokenizer = createTokenizer(data), cat, id, file = new Text.File(data), block = new Text.DataBlock(data, "default"), saveFrame = new Text.DataBlock(data, "empty"), inSaveFrame = false, blockSaveFrames;
moveNext(tokenizer);
while (tokenizer.currentTokenType !== 6 /* End */) {
var token = tokenizer.currentTokenType;
// Data block
if (token === 0 /* Data */) {
if (inSaveFrame) {
return error(tokenizer.currentLineNumber, "Unexpected data block inside a save frame.");
}
if (block.categories.length > 0) {
file.dataBlocks.push(block);
}
block = new Text.DataBlock(data, data.substring(tokenizer.currentTokenStart + 5, tokenizer.currentTokenEnd));
moveNext(tokenizer);
// Save frame
}
else if (token === 1 /* Save */) {
id = data.substring(tokenizer.currentTokenStart + 5, tokenizer.currentTokenEnd);
if (id.length === 0) {
if (saveFrame.categories.length > 0) {
blockSaveFrames = block.additionalData["saveFrames"];
if (!blockSaveFrames) {
blockSaveFrames = [];
block.additionalData["saveFrames"] = blockSaveFrames;
}
blockSaveFrames[blockSaveFrames.length] = saveFrame;
}
inSaveFrame = false;
}
else {
if (inSaveFrame) {
return error(tokenizer.currentLineNumber, "Save frames cannot be nested.");
}
inSaveFrame = true;
saveFrame = new Text.DataBlock(data, id);
}
moveNext(tokenizer);
// Loop
}
else if (token === 2 /* Loop */) {
cat = handleLoop(tokenizer, inSaveFrame ? saveFrame : block);
if (cat.hasError) {
return error(cat.errorLine, cat.errorMessage);
}
// Single row
}
else if (token === 4 /* ColumnName */) {
cat = handleSingle(tokenizer, inSaveFrame ? saveFrame : block);
if (cat.hasError) {
return error(cat.errorLine, cat.errorMessage);
}
// Out of options
}
else {
return error(tokenizer.currentLineNumber, "Unexpected token. Expected data_, loop_, or data name.");
}
}
// Check if the latest save frame was closed.
if (inSaveFrame) {
return error(tokenizer.currentLineNumber, "Unfinished save frame (`" + saveFrame.header + "`).");
}
if (block.categories.length > 0) {
file.dataBlocks.push(block);
}
return result(file);
}
function parse(data) {
return parseInternal(data);
}
Text.parse = parse;
})(Text = CIFTools.Text || (CIFTools.Text = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Text;
(function (Text) {
"use strict";
var StringWriter = CIFTools.Utils.StringWriter;
var Writer = /** @class */ (function () {
function Writer() {
this.writer = StringWriter.create();
this.encoded = false;
this.dataBlockCreated = false;
}
Writer.prototype.startDataBlock = function (header) {
this.dataBlockCreated = true;
StringWriter.write(this.writer, "data_" + (header || '').replace(/[ \n\t]/g, '').toUpperCase() + "\n#\n");
};
Writer.prototype.writeCategory = function (category, contexts) {
if (this.encoded) {
throw new Error('The writer contents have already been encoded, no more writing.');
}
if (!this.dataBlockCreated) {
throw new Error('No data block created.');
}
var src = !contexts || !contexts.length ? [category(void 0)] : contexts.map(function (c) { return category(c); });
var data = src.filter(function (c) { return c && c.count > 0; });
if (!data.length)
return;
var count = data.reduce(function (a, c) { return a + (c.count === void 0 ? 1 : c.count); }, 0);
if (!count)
return;
else if (count === 1) {
writeCifSingleRecord(data[0], this.writer);
}
else {
writeCifLoop(data, this.writer);
}
};
Writer.prototype.encode = function () {
this.encoded = true;
};
Writer.prototype.flush = function (stream) {
StringWriter.writeTo(this.writer, stream);
};
return Writer;
}());
Text.Writer = Writer;
function isMultiline(value) {
return !!value && value.indexOf('\n') >= 0;
}
function writeCifSingleRecord(category, writer) {
var fields = category.desc.fields;
var data = category.data;
var width = fields.reduce(function (w, s) { return Math.max(w, s.name.length); }, 0) + category.desc.name.length + 5;
for (var _i = 0, fields_1 = fields; _i < fields_1.length; _i++) {
var f = fields_1[_i];
StringWriter.writePadRight(writer, category.desc.name + "." + f.name, width);
var presence = f.presence;
var p = presence ? presence(data, 0) : 0 /* Present */;
if (p !== 0 /* Present */) {
if (p === 1 /* NotSpecified */)
writeNotSpecified(writer);
else
writeUnknown(writer);
}
else {
var val = f.string(data, 0);
if (isMultiline(val)) {
writeMultiline(writer, val);
StringWriter.newline(writer);
}
else {
writeChecked(writer, val);
}
}
StringWriter.newline(writer);
}
StringWriter.write(writer, '#\n');
}
function writeCifLoop(categories, writer) {
writeLine(writer, 'loop_');
var first = categories[0];
var fields = first.desc.fields;
for (var _i = 0, fields_2 = fields; _i < fields_2.length; _i++) {
var f = fields_2[_i];
writeLine(writer, first.desc.name + "." + f.name);
}
for (var _a = 0, categories_1 = categories; _a < categories_1.length; _a++) {
var category = categories_1[_a];
var data = category.data;
var count = category.count;
for (var i = 0; i < count; i++) {
for (var _b = 0, fields_3 = fields; _b < fields_3.length; _b++) {
var f = fields_3[_b];
var presence = f.presence;
var p = presence ? presence(data, i) : 0 /* Present */;
if (p !== 0 /* Present */) {
if (p === 1 /* NotSpecified */)
writeNotSpecified(writer);
else
writeUnknown(writer);
}
else {
var val = f.string(data, i);
if (isMultiline(val)) {
writeMultiline(writer, val);
StringWriter.newline(writer);
}
else {
writeChecked(writer, val);
}
}
}
StringWriter.newline(writer);
}
}
StringWriter.write(writer, '#\n');
}
function writeLine(writer, val) {
StringWriter.write(writer, val);
StringWriter.newline(writer);
}
function writeInteger(writer, val) {
StringWriter.writeSafe(writer, '' + val + ' ');
}
/**
* eg writeFloat(123.2123, 100) -- 2 decim
*/
function writeFloat(writer, val, precisionMultiplier) {
StringWriter.writeSafe(writer, '' + Math.round(precisionMultiplier * val) / precisionMultiplier + ' ');
}
/**
* Writes '. '
*/
function writeNotSpecified(writer) {
StringWriter.writeSafe(writer, '. ');
}
/**
* Writes '? '
*/
function writeUnknown(writer) {
StringWriter.writeSafe(writer, '? ');
}
function writeChecked(writer, val) {
if (!val) {
StringWriter.writeSafe(writer, '. ');
return;
}
var escape = false, escapeCharStart = '\'', escapeCharEnd = '\' ';
var hasWhitespace = false;
var hasSingle = false;
var hasDouble = false;
for (var i = 0, _l = val.length - 1; i < _l; i++) {
var c = val.charCodeAt(i);
switch (c) {
case 9:
hasWhitespace = true;
break; // \t
case 10: // \n
StringWriter.writeSafe(writer, '\n;' + val);
StringWriter.writeSafe(writer, '\n; ');
return;
case 32:
hasWhitespace = true;
break; // ' '
case 34: // "
if (hasSingle) {
StringWriter.writeSafe(writer, '\n;' + val);
StringWriter.writeSafe(writer, '\n; ');
return;
}
hasDouble = true;
escape = true;
escapeCharStart = '\'';
escapeCharEnd = '\' ';
break;
case 39: // '
if (hasDouble) {
StringWriter.writeSafe(writer, '\n;' + val);
StringWriter.writeSafe(writer, '\n; ');
return;
}
escape = true;
hasSingle = true;
escapeCharStart = '"';
escapeCharEnd = '" ';
break;
}
}
var fst = val.charCodeAt(0);
if (!escape && (fst === 35 /* # */ || fst === 36 /* $ */ || fst === 59 /* ; */ || fst === 91 /* [ */ || fst === 93 /* ] */ || hasWhitespace)) {
escapeCharStart = '\'';
escapeCharEnd = '\' ';
escape = true;
}
if (escape) {
StringWriter.writeSafe(writer, escapeCharStart + val + escapeCharEnd);
}
else {
StringWriter.write(writer, val);
StringWriter.writeSafe(writer, ' ');
}
}
function writeMultiline(writer, val) {
StringWriter.writeSafe(writer, '\n;' + val);
StringWriter.writeSafe(writer, '\n; ');
}
function writeToken(writer, data, start, end) {
var escape = false, escapeCharStart = '\'', escapeCharEnd = '\' ';
for (var i = start; i < end - 1; i++) {
var c = data.charCodeAt(i);
switch (c) {
case 10: // \n
StringWriter.writeSafe(writer, '\n;' + data.substring(start, end));
StringWriter.writeSafe(writer, '\n; ');
return;
case 34: // "
escape = true;
escapeCharStart = '\'';
escapeCharEnd = '\' ';
break;
case 39: // '
escape = true;
escapeCharStart = '"';
escapeCharEnd = '" ';
break;
}
}
if (!escape && data.charCodeAt(start) === 59 /* ; */) {
escapeCharStart = '\'';
escapeCharEnd = '\' ';
escape = true;
}
if (escape) {
StringWriter.writeSafe(writer, escapeCharStart + data.substring(start, end));
StringWriter.writeSafe(writer, escapeCharStart);
}
else {
StringWriter.write(writer, data.substring(start, end));
StringWriter.writeSafe(writer, ' ');
}
}
})(Text = CIFTools.Text || (CIFTools.Text = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
var Binary;
(function (Binary) {
var MessagePack;
(function (MessagePack) {
/*
* Adapted from https://github.com/rcsb/mmtf-javascript
* by Alexander Rose , MIT License, Copyright (c) 2016
*/
/**
* decode all key-value pairs of a map into an object
* @param {Integer} length - number of key-value pairs
* @return {Object} decoded map
*/
function map(state, length) {
var value = {};
for (var i = 0; i < length; i++) {
var key = parse(state);
value[key] = parse(state);
}
return value;
}
/**
* decode binary array
* @param {Integer} length - number of elements in the array
* @return {Uint8Array} decoded array
*/
function bin(state, length) {
// This approach to binary parsing wastes a bit of memory to trade for speed compared to:
//
// let value = buffer.subarray(offset, offset + length); //new Uint8Array(buffer.buffer, offset, length);
//
// It turns out that using the view created by subarray probably uses DataView
// in the background, which causes the element access to be several times slower
// than creating the new byte array.
var value = new Uint8Array(length);
var o = state.offset;
for (var i = 0; i < length; i++)
value[i] = state.buffer[i + o];
state.offset += length;
return value;
}
/**
* decode string
* @param {Integer} length - number string characters
* @return {String} decoded string
*/
function str(state, length) {
var value = MessagePack.utf8Read(state.buffer, state.offset, length);
state.offset += length;
return value;
}
/**
* decode array
* @param {Integer} length - number of array elements
* @return {Array} decoded array
*/
function array(state, length) {
var value = new Array(length);
for (var i = 0; i < length; i++) {
value[i] = parse(state);
}
return value;
}
/**
* recursively parse the MessagePack data
* @return {Object|Array|String|Number|Boolean|null} decoded MessagePack data
*/
function parse(state) {
var type = state.buffer[state.offset];
var value, length;
// Positive FixInt
if ((type & 0x80) === 0x00) {
state.offset++;
return type;
}
// FixMap
if ((type & 0xf0) === 0x80) {
length = type & 0x0f;
state.offset++;
return map(state, length);
}
// FixArray
if ((type & 0xf0) === 0x90) {
length = type & 0x0f;
state.offset++;
return array(state, length);
}
// FixStr
if ((type & 0xe0) === 0xa0) {
length = type & 0x1f;
state.offset++;
return str(state, length);
}
// Negative FixInt
if ((type & 0xe0) === 0xe0) {
value = state.dataView.getInt8(state.offset);
state.offset++;
return value;
}
switch (type) {
// nil
case 0xc0:
state.offset++;
return null;
// false
case 0xc2:
state.offset++;
return false;
// true
case 0xc3:
state.offset++;
return true;
// bin 8
case 0xc4:
length = state.dataView.getUint8(state.offset + 1);
state.offset += 2;
return bin(state, length);
// bin 16
case 0xc5:
length = state.dataView.getUint16(state.offset + 1);
state.offset += 3;
return bin(state, length);
// bin 32
case 0xc6:
length = state.dataView.getUint32(state.offset + 1);
state.offset += 5;
return bin(state, length);
// float 32
case 0xca:
value = state.dataView.getFloat32(state.offset + 1);
state.offset += 5;
return value;
// float 64
case 0xcb:
value = state.dataView.getFloat64(state.offset + 1);
state.offset += 9;
return value;
// uint8
case 0xcc:
value = state.buffer[state.offset + 1];
state.offset += 2;
return value;
// uint 16
case 0xcd:
value = state.dataView.getUint16(state.offset + 1);
state.offset += 3;
return value;
// uint 32
case 0xce:
value = state.dataView.getUint32(state.offset + 1);
state.offset += 5;
return value;
// int 8
case 0xd0:
value = state.dataView.getInt8(state.offset + 1);
state.offset += 2;
return value;
// int 16
case 0xd1:
value = state.dataView.getInt16(state.offset + 1);
state.offset += 3;
return value;
// int 32
case 0xd2:
value = state.dataView.getInt32(state.offset + 1);
state.offset += 5;
return value;
// str 8
case 0xd9:
length = state.dataView.getUint8(state.offset + 1);
state.offset += 2;
return str(state, length);
// str 16
case 0xda:
length = state.dataView.getUint16(state.offset + 1);
state.offset += 3;
return str(state, length);
// str 32
case 0xdb:
length = state.dataView.getUint32(state.offset + 1);
state.offset += 5;
return str(state, length);
// array 16
case 0xdc:
length = state.dataView.getUint16(state.offset + 1);
state.offset += 3;
return array(state, length);
// array 32
case 0xdd:
length = state.dataView.getUint32(state.offset + 1);
state.offset += 5;
return array(state, length);
// map 16:
case 0xde:
length = state.dataView.getUint16(state.offset + 1);
state.offset += 3;
return map(state, length);
// map 32
case 0xdf:
length = state.dataView.getUint32(state.offset + 1);
state.offset += 5;
return map(state, length);
}
throw new Error("Unknown type 0x" + type.toString(16));
}
function decode(buffer) {
return parse({
buffer: buffer,
offset: 0,
dataView: new DataView(buffer.buffer)
});
}
MessagePack.decode = decode;
})(MessagePack = Binary.MessagePack || (Binary.MessagePack = {}));
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
var MessagePack;
(function (MessagePack) {
/*
* Adapted from https://github.com/rcsb/mmtf-javascript
* by Alexander Rose , MIT License, Copyright (c) 2016
*/
function encode(value) {
var buffer = new ArrayBuffer(encodedSize(value));
var view = new DataView(buffer);
var bytes = new Uint8Array(buffer);
encodeInternal(value, view, bytes, 0);
return bytes;
}
MessagePack.encode = encode;
function encodedSize(value) {
var type = typeof value;
// Raw Bytes
if (type === "string") {
var length_1 = MessagePack.utf8ByteCount(value);
if (length_1 < 0x20) {
return 1 + length_1;
}
if (length_1 < 0x100) {
return 2 + length_1;
}
if (length_1 < 0x10000) {
return 3 + length_1;
}
if (length_1 < 0x100000000) {
return 5 + length_1;
}
}
if (value instanceof Uint8Array) {
var length_2 = value.byteLength;
if (length_2 < 0x100) {
return 2 + length_2;
}
if (length_2 < 0x10000) {
return 3 + length_2;
}
if (length_2 < 0x100000000) {
return 5 + length_2;
}
}
if (type === "number") {
// Floating Point
// double
if (Math.floor(value) !== value)
return 9;
// Integers
if (value >= 0) {
// positive fixnum
if (value < 0x80)
return 1;
// uint 8
if (value < 0x100)
return 2;
// uint 16
if (value < 0x10000)
return 3;
// uint 32
if (value < 0x100000000)
return 5;
throw new Error("Number too big 0x" + value.toString(16));
}
// negative fixnum
if (value >= -0x20)
return 1;
// int 8
if (value >= -0x80)
return 2;
// int 16
if (value >= -0x8000)
return 3;
// int 32
if (value >= -0x80000000)
return 5;
throw new Error("Number too small -0x" + value.toString(16).substr(1));
}
// Boolean, null
if (type === "boolean" || value === null || value === void 0)
return 1;
// Container Types
if (type === "object") {
var length_3, size = 0;
if (Array.isArray(value)) {
length_3 = value.length;
for (var i = 0; i < length_3; i++) {
size += encodedSize(value[i]);
}
}
else {
var keys = Object.keys(value);
length_3 = keys.length;
for (var i = 0; i < length_3; i++) {
var key = keys[i];
size += encodedSize(key) + encodedSize(value[key]);
}
}
if (length_3 < 0x10) {
return 1 + size;
}
if (length_3 < 0x10000) {
return 3 + size;
}
if (length_3 < 0x100000000) {
return 5 + size;
}
throw new Error("Array or object too long 0x" + length_3.toString(16));
}
throw new Error("Unknown type " + type);
}
function encodeInternal(value, view, bytes, offset) {
var type = typeof value;
// Strings Bytes
if (type === "string") {
var length_4 = MessagePack.utf8ByteCount(value);
// fix str
if (length_4 < 0x20) {
view.setUint8(offset, length_4 | 0xa0);
MessagePack.utf8Write(bytes, offset + 1, value);
return 1 + length_4;
}
// str 8
if (length_4 < 0x100) {
view.setUint8(offset, 0xd9);
view.setUint8(offset + 1, length_4);
MessagePack.utf8Write(bytes, offset + 2, value);
return 2 + length_4;
}
// str 16
if (length_4 < 0x10000) {
view.setUint8(offset, 0xda);
view.setUint16(offset + 1, length_4);
MessagePack.utf8Write(bytes, offset + 3, value);
return 3 + length_4;
}
// str 32
if (length_4 < 0x100000000) {
view.setUint8(offset, 0xdb);
view.setUint32(offset + 1, length_4);
MessagePack.utf8Write(bytes, offset + 5, value);
return 5 + length_4;
}
}
if (value instanceof Uint8Array) {
var length_5 = value.byteLength;
var bytes_1 = new Uint8Array(view.buffer);
// bin 8
if (length_5 < 0x100) {
view.setUint8(offset, 0xc4);
view.setUint8(offset + 1, length_5);
bytes_1.set(value, offset + 2);
return 2 + length_5;
}
// bin 16
if (length_5 < 0x10000) {
view.setUint8(offset, 0xc5);
view.setUint16(offset + 1, length_5);
bytes_1.set(value, offset + 3);
return 3 + length_5;
}
// bin 32
if (length_5 < 0x100000000) {
view.setUint8(offset, 0xc6);
view.setUint32(offset + 1, length_5);
bytes_1.set(value, offset + 5);
return 5 + length_5;
}
}
if (type === "number") {
if (!isFinite(value)) {
throw new Error("Number not finite: " + value);
}
// Floating point
if (Math.floor(value) !== value) {
view.setUint8(offset, 0xcb);
view.setFloat64(offset + 1, value);
return 9;
}
// Integers
if (value >= 0) {
// positive fixnum
if (value < 0x80) {
view.setUint8(offset, value);
return 1;
}
// uint 8
if (value < 0x100) {
view.setUint8(offset, 0xcc);
view.setUint8(offset + 1, value);
return 2;
}
// uint 16
if (value < 0x10000) {
view.setUint8(offset, 0xcd);
view.setUint16(offset + 1, value);
return 3;
}
// uint 32
if (value < 0x100000000) {
view.setUint8(offset, 0xce);
view.setUint32(offset + 1, value);
return 5;
}
throw new Error("Number too big 0x" + value.toString(16));
}
// negative fixnum
if (value >= -0x20) {
view.setInt8(offset, value);
return 1;
}
// int 8
if (value >= -0x80) {
view.setUint8(offset, 0xd0);
view.setInt8(offset + 1, value);
return 2;
}
// int 16
if (value >= -0x8000) {
view.setUint8(offset, 0xd1);
view.setInt16(offset + 1, value);
return 3;
}
// int 32
if (value >= -0x80000000) {
view.setUint8(offset, 0xd2);
view.setInt32(offset + 1, value);
return 5;
}
throw new Error("Number too small -0x" + (-value).toString(16).substr(1));
}
// null
if (value === null || value === undefined) {
view.setUint8(offset, 0xc0);
return 1;
}
// Boolean
if (type === "boolean") {
view.setUint8(offset, value ? 0xc3 : 0xc2);
return 1;
}
// Container Types
if (type === "object") {
var length_6, size = 0;
var isArray = Array.isArray(value);
var keys = void 0;
if (isArray) {
length_6 = value.length;
}
else {
keys = Object.keys(value);
length_6 = keys.length;
}
if (length_6 < 0x10) {
view.setUint8(offset, length_6 | (isArray ? 0x90 : 0x80));
size = 1;
}
else if (length_6 < 0x10000) {
view.setUint8(offset, isArray ? 0xdc : 0xde);
view.setUint16(offset + 1, length_6);
size = 3;
}
else if (length_6 < 0x100000000) {
view.setUint8(offset, isArray ? 0xdd : 0xdf);
view.setUint32(offset + 1, length_6);
size = 5;
}
if (isArray) {
for (var i = 0; i < length_6; i++) {
size += encodeInternal(value[i], view, bytes, offset + size);
}
}
else {
for (var _i = 0, _a = keys; _i < _a.length; _i++) {
var key = _a[_i];
size += encodeInternal(key, view, bytes, offset + size);
size += encodeInternal(value[key], view, bytes, offset + size);
}
}
return size;
}
throw new Error("Unknown type " + type);
}
})(MessagePack = Binary.MessagePack || (Binary.MessagePack = {}));
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
var MessagePack;
(function (MessagePack) {
/*
* Adapted from https://github.com/rcsb/mmtf-javascript
* by Alexander Rose , MIT License, Copyright (c) 2016
*/
function utf8Write(data, offset, str) {
var byteLength = data.byteLength;
for (var i = 0, l = str.length; i < l; i++) {
var codePoint = str.charCodeAt(i);
// One byte of UTF-8
if (codePoint < 0x80) {
data[offset++] = codePoint >>> 0 & 0x7f | 0x00;
continue;
}
// Two bytes of UTF-8
if (codePoint < 0x800) {
data[offset++] = codePoint >>> 6 & 0x1f | 0xc0;
data[offset++] = codePoint >>> 0 & 0x3f | 0x80;
continue;
}
// Three bytes of UTF-8.
if (codePoint < 0x10000) {
data[offset++] = codePoint >>> 12 & 0x0f | 0xe0;
data[offset++] = codePoint >>> 6 & 0x3f | 0x80;
data[offset++] = codePoint >>> 0 & 0x3f | 0x80;
continue;
}
// Four bytes of UTF-8
if (codePoint < 0x110000) {
data[offset++] = codePoint >>> 18 & 0x07 | 0xf0;
data[offset++] = codePoint >>> 12 & 0x3f | 0x80;
data[offset++] = codePoint >>> 6 & 0x3f | 0x80;
data[offset++] = codePoint >>> 0 & 0x3f | 0x80;
continue;
}
throw new Error("bad codepoint " + codePoint);
}
}
MessagePack.utf8Write = utf8Write;
var __chars = function () {
var data = [];
for (var i = 0; i < 1024; i++)
data[i] = String.fromCharCode(i);
return data;
}();
function throwError(err) {
throw new Error(err);
}
function utf8Read(data, offset, length) {
var chars = __chars;
var str = void 0, chunk = [], chunkSize = 512, chunkOffset = 0;
for (var i = offset, end = offset + length; i < end; i++) {
var byte = data[i];
// One byte character
if ((byte & 0x80) === 0x00) {
chunk[chunkOffset++] = chars[byte];
}
// Two byte character
else if ((byte & 0xe0) === 0xc0) {
chunk[chunkOffset++] = chars[((byte & 0x0f) << 6) | (data[++i] & 0x3f)];
}
// Three byte character
else if ((byte & 0xf0) === 0xe0) {
chunk[chunkOffset++] = String.fromCharCode(((byte & 0x0f) << 12) |
((data[++i] & 0x3f) << 6) |
((data[++i] & 0x3f) << 0));
}
// Four byte character
else if ((byte & 0xf8) === 0xf0) {
chunk[chunkOffset++] = String.fromCharCode(((byte & 0x07) << 18) |
((data[++i] & 0x3f) << 12) |
((data[++i] & 0x3f) << 6) |
((data[++i] & 0x3f) << 0));
}
else
throwError("Invalid byte " + byte.toString(16));
if (chunkOffset === chunkSize) {
str = str || [];
str[str.length] = chunk.join('');
chunkOffset = 0;
}
}
if (!str)
return chunk.slice(0, chunkOffset).join('');
if (chunkOffset > 0) {
str[str.length] = chunk.slice(0, chunkOffset).join('');
}
return str.join('');
}
MessagePack.utf8Read = utf8Read;
function utf8ByteCount(str) {
var count = 0;
for (var i = 0, l = str.length; i < l; i++) {
var codePoint = str.charCodeAt(i);
if (codePoint < 0x80) {
count += 1;
continue;
}
if (codePoint < 0x800) {
count += 2;
continue;
}
if (codePoint < 0x10000) {
count += 3;
continue;
}
if (codePoint < 0x110000) {
count += 4;
continue;
}
throwError("bad codepoint " + codePoint);
}
return count;
}
MessagePack.utf8ByteCount = utf8ByteCount;
})(MessagePack = Binary.MessagePack || (Binary.MessagePack = {}));
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
"use strict";
/**
* Fixed point, delta, RLE, integer packing adopted from https://github.com/rcsb/mmtf-javascript/
* by Alexander Rose , MIT License, Copyright (c) 2016
*/
function decode(data) {
var current = data.data;
for (var i = data.encoding.length - 1; i >= 0; i--) {
current = Decoder.decodeStep(current, data.encoding[i]);
}
return current;
}
Binary.decode = decode;
var Decoder;
(function (Decoder) {
function decodeStep(data, encoding) {
switch (encoding.kind) {
case 'ByteArray': {
switch (encoding.type) {
case 4 /* Uint8 */: return data;
case 1 /* Int8 */: return int8(data);
case 2 /* Int16 */: return int16(data);
case 5 /* Uint16 */: return uint16(data);
case 3 /* Int32 */: return int32(data);
case 6 /* Uint32 */: return uint32(data);
case 32 /* Float32 */: return float32(data);
case 33 /* Float64 */: return float64(data);
default: throw new Error('Unsupported ByteArray type.');
}
}
case 'FixedPoint': return fixedPoint(data, encoding);
case 'IntervalQuantization': return intervalQuantization(data, encoding);
case 'RunLength': return runLength(data, encoding);
case 'Delta': return delta(data, encoding);
case 'IntegerPacking': return integerPacking(data, encoding);
case 'StringArray': return stringArray(data, encoding);
}
}
Decoder.decodeStep = decodeStep;
function getIntArray(type, size) {
switch (type) {
case 1 /* Int8 */: return new Int8Array(size);
case 2 /* Int16 */: return new Int16Array(size);
case 3 /* Int32 */: return new Int32Array(size);
case 4 /* Uint8 */: return new Uint8Array(size);
case 5 /* Uint16 */: return new Uint16Array(size);
case 6 /* Uint32 */: return new Uint32Array(size);
default: throw new Error('Unsupported integer data type.');
}
}
function getFloatArray(type, size) {
switch (type) {
case 32 /* Float32 */: return new Float32Array(size);
case 33 /* Float64 */: return new Float64Array(size);
default: throw new Error('Unsupported floating data type.');
}
}
/* http://stackoverflow.com/questions/7869752/javascript-typed-arrays-and-endianness */
var isLittleEndian = (function () {
var arrayBuffer = new ArrayBuffer(2);
var uint8Array = new Uint8Array(arrayBuffer);
var uint16array = new Uint16Array(arrayBuffer);
uint8Array[0] = 0xAA;
uint8Array[1] = 0xBB;
if (uint16array[0] === 0xBBAA)
return true;
return false;
})();
function int8(data) { return new Int8Array(data.buffer, data.byteOffset); }
function flipByteOrder(data, bytes) {
var buffer = new ArrayBuffer(data.length);
var ret = new Uint8Array(buffer);
for (var i = 0, n = data.length; i < n; i += bytes) {
for (var j = 0; j < bytes; j++) {
ret[i + bytes - j - 1] = data[i + j];
}
}
return buffer;
}
function view(data, byteSize, c) {
if (isLittleEndian)
return new c(data.buffer);
return new c(flipByteOrder(data, byteSize));
}
function int16(data) { return view(data, 2, Int16Array); }
function uint16(data) { return view(data, 2, Uint16Array); }
function int32(data) { return view(data, 4, Int32Array); }
function uint32(data) { return view(data, 4, Uint32Array); }
function float32(data) { return view(data, 4, Float32Array); }
function float64(data) { return view(data, 8, Float64Array); }
function fixedPoint(data, encoding) {
var n = data.length;
var output = getFloatArray(encoding.srcType, n);
var f = 1 / encoding.factor;
for (var i = 0; i < n; i++) {
output[i] = f * data[i];
}
return output;
}
function intervalQuantization(data, encoding) {
var n = data.length;
var output = getFloatArray(encoding.srcType, n);
var delta = (encoding.max - encoding.min) / (encoding.numSteps - 1);
var min = encoding.min;
for (var i = 0; i < n; i++) {
output[i] = min + delta * data[i];
}
return output;
}
function runLength(data, encoding) {
var output = getIntArray(encoding.srcType, encoding.srcSize);
var dataOffset = 0;
for (var i = 0, il = data.length; i < il; i += 2) {
var value = data[i]; // value to be repeated
var length_7 = data[i + 1]; // number of repeats
for (var j = 0; j < length_7; ++j) {
output[dataOffset++] = value;
}
}
return output;
}
function delta(data, encoding) {
var n = data.length;
var output = getIntArray(encoding.srcType, n);
if (!n)
return output;
output[0] = data[0] + (encoding.origin | 0);
for (var i = 1; i < n; ++i) {
output[i] = data[i] + output[i - 1];
}
return output;
}
function integerPackingSigned(data, encoding) {
var upperLimit = encoding.byteCount === 1 ? 0x7F : 0x7FFF;
var lowerLimit = -upperLimit - 1;
var n = data.length;
var output = new Int32Array(encoding.srcSize);
var i = 0;
var j = 0;
while (i < n) {
var value = 0, t = data[i];
while (t === upperLimit || t === lowerLimit) {
value += t;
i++;
t = data[i];
}
value += t;
output[j] = value;
i++;
j++;
}
return output;
}
function integerPackingUnsigned(data, encoding) {
var upperLimit = encoding.byteCount === 1 ? 0xFF : 0xFFFF;
var n = data.length;
var output = new Int32Array(encoding.srcSize);
var i = 0;
var j = 0;
while (i < n) {
var value = 0, t = data[i];
while (t === upperLimit) {
value += t;
i++;
t = data[i];
}
value += t;
output[j] = value;
i++;
j++;
}
return output;
}
function integerPacking(data, encoding) {
return encoding.isUnsigned ? integerPackingUnsigned(data, encoding) : integerPackingSigned(data, encoding);
}
function stringArray(data, encoding) {
var str = encoding.stringData;
var offsets = decode({ encoding: encoding.offsetEncoding, data: encoding.offsets });
var indices = decode({ encoding: encoding.dataEncoding, data: data });
var cache = Object.create(null);
var result = new Array(indices.length);
var offset = 0;
for (var _i = 0, indices_1 = indices; _i < indices_1.length; _i++) {
var i = indices_1[_i];
if (i < 0) {
result[offset++] = null;
continue;
}
var v = cache[i];
if (v === void 0) {
v = str.substring(offsets[i], offsets[i + 1]);
cache[i] = v;
}
result[offset++] = v;
}
return result;
}
})(Decoder || (Decoder = {}));
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
"use strict";
var File = /** @class */ (function () {
function File(data) {
this.dataBlocks = data.dataBlocks.map(function (b) { return new DataBlock(b); });
}
File.prototype.toJSON = function () {
return this.dataBlocks.map(function (b) { return b.toJSON(); });
};
return File;
}());
Binary.File = File;
var DataBlock = /** @class */ (function () {
function DataBlock(data) {
this.additionalData = {};
this.header = data.header;
this.categoryList = data.categories.map(function (c) { return new Category(c); });
this.categoryMap = new Map();
for (var _i = 0, _a = this.categoryList; _i < _a.length; _i++) {
var c = _a[_i];
this.categoryMap.set(c.name, c);
}
}
Object.defineProperty(DataBlock.prototype, "categories", {
get: function () { return this.categoryList; },
enumerable: true,
configurable: true
});
DataBlock.prototype.getCategory = function (name) { return this.categoryMap.get(name); };
DataBlock.prototype.toJSON = function () {
return {
id: this.header,
categories: this.categoryList.map(function (c) { return c.toJSON(); }),
additionalData: this.additionalData
};
};
return DataBlock;
}());
Binary.DataBlock = DataBlock;
var Category = /** @class */ (function () {
function Category(data) {
this.name = data.name;
this.columnCount = data.columns.length;
this.rowCount = data.rowCount;
this.columnNameList = [];
this.encodedColumns = new Map();
for (var _i = 0, _a = data.columns; _i < _a.length; _i++) {
var c = _a[_i];
this.encodedColumns.set(c.name, c);
this.columnNameList.push(c.name);
}
}
Object.defineProperty(Category.prototype, "columnNames", {
get: function () { return this.columnNameList; },
enumerable: true,
configurable: true
});
Category.prototype.getColumn = function (name) {
var w = this.encodedColumns.get(name);
if (w)
return wrapColumn(w);
return CIFTools.UndefinedColumn;
};
Category.prototype.toJSON = function () {
var _this = this;
var rows = [];
var columns = this.columnNameList.map(function (name) { return ({ name: name, column: _this.getColumn(name) }); });
for (var i = 0; i < this.rowCount; i++) {
var item = {};
for (var _i = 0, columns_1 = columns; _i < columns_1.length; _i++) {
var c = columns_1[_i];
var d = c.column.getValuePresence(i);
if (d === 0 /* Present */)
item[c.name] = c.column.getString(i);
else if (d === 1 /* NotSpecified */)
item[c.name] = '.';
else
item[c.name] = '?';
}
rows[i] = item;
}
return { name: this.name, columns: this.columnNames, rows: rows };
};
return Category;
}());
Binary.Category = Category;
function wrapColumn(column) {
if (!column.data.data)
return CIFTools.UndefinedColumn;
var data = Binary.decode(column.data);
var mask = void 0;
if (column.mask)
mask = Binary.decode(column.mask);
if (data.buffer && data.byteLength && data.BYTES_PER_ELEMENT) {
return mask ? new MaskedNumericColumn(data, mask) : new NumericColumn(data);
}
return mask ? new MaskedStringColumn(data, mask) : new StringColumn(data);
}
var fastParseInt = CIFTools.Utils.FastNumberParsers.parseInt;
var fastParseFloat = CIFTools.Utils.FastNumberParsers.parseFloat;
var NumericColumn = /** @class */ (function () {
function NumericColumn(data) {
this.data = data;
this.isDefined = true;
}
NumericColumn.prototype.getString = function (row) { return "" + this.data[row]; };
NumericColumn.prototype.getInteger = function (row) { return this.data[row] | 0; };
NumericColumn.prototype.getFloat = function (row) { return 1.0 * this.data[row]; };
NumericColumn.prototype.stringEquals = function (row, value) { return this.data[row] === fastParseFloat(value, 0, value.length); };
NumericColumn.prototype.areValuesEqual = function (rowA, rowB) { return this.data[rowA] === this.data[rowB]; };
NumericColumn.prototype.getValuePresence = function (row) { return 0 /* Present */; };
return NumericColumn;
}());
var MaskedNumericColumn = /** @class */ (function () {
function MaskedNumericColumn(data, mask) {
this.data = data;
this.mask = mask;
this.isDefined = true;
}
MaskedNumericColumn.prototype.getString = function (row) { return this.mask[row] === 0 /* Present */ ? "" + this.data[row] : null; };
MaskedNumericColumn.prototype.getInteger = function (row) { return this.mask[row] === 0 /* Present */ ? this.data[row] : 0; };
MaskedNumericColumn.prototype.getFloat = function (row) { return this.mask[row] === 0 /* Present */ ? this.data[row] : 0; };
MaskedNumericColumn.prototype.stringEquals = function (row, value) { return this.mask[row] === 0 /* Present */ ? this.data[row] === fastParseFloat(value, 0, value.length) : value === null || value === void 0; };
MaskedNumericColumn.prototype.areValuesEqual = function (rowA, rowB) { return this.data[rowA] === this.data[rowB]; };
MaskedNumericColumn.prototype.getValuePresence = function (row) { return this.mask[row]; };
return MaskedNumericColumn;
}());
var StringColumn = /** @class */ (function () {
function StringColumn(data) {
this.data = data;
this.isDefined = true;
}
StringColumn.prototype.getString = function (row) { return this.data[row]; };
StringColumn.prototype.getInteger = function (row) { var v = this.data[row]; return fastParseInt(v, 0, v.length); };
StringColumn.prototype.getFloat = function (row) { var v = this.data[row]; return fastParseFloat(v, 0, v.length); };
StringColumn.prototype.stringEquals = function (row, value) { return this.data[row] === value; };
StringColumn.prototype.areValuesEqual = function (rowA, rowB) { return this.data[rowA] === this.data[rowB]; };
StringColumn.prototype.getValuePresence = function (row) { return 0 /* Present */; };
return StringColumn;
}());
var MaskedStringColumn = /** @class */ (function () {
function MaskedStringColumn(data, mask) {
this.data = data;
this.mask = mask;
this.isDefined = true;
}
MaskedStringColumn.prototype.getString = function (row) { return this.mask[row] === 0 /* Present */ ? this.data[row] : null; };
MaskedStringColumn.prototype.getInteger = function (row) { if (this.mask[row] !== 0 /* Present */)
return 0; var v = this.data[row]; return fastParseInt(v || '', 0, (v || '').length); };
MaskedStringColumn.prototype.getFloat = function (row) { if (this.mask[row] !== 0 /* Present */)
return 0; var v = this.data[row]; return fastParseFloat(v || '', 0, (v || '').length); };
MaskedStringColumn.prototype.stringEquals = function (row, value) { return this.data[row] === value; };
MaskedStringColumn.prototype.areValuesEqual = function (rowA, rowB) { return this.data[rowA] === this.data[rowB]; };
MaskedStringColumn.prototype.getValuePresence = function (row) { return this.mask[row]; };
return MaskedStringColumn;
}());
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
"use strict";
/**
* Fixed point, delta, RLE, integer packing adopted from https://github.com/rcsb/mmtf-javascript/
* by Alexander Rose , MIT License, Copyright (c) 2016
*/
var Encoder = /** @class */ (function () {
function Encoder(providers) {
this.providers = providers;
}
Encoder.prototype.and = function (f) {
return new Encoder(this.providers.concat([f]));
};
Encoder.prototype.encode = function (data) {
var encoding = [];
for (var _i = 0, _a = this.providers; _i < _a.length; _i++) {
var p = _a[_i];
var t = p(data);
if (!t.encodings.length) {
throw new Error('Encodings must be non-empty.');
}
data = t.data;
for (var _b = 0, _c = t.encodings; _b < _c.length; _b++) {
var e = _c[_b];
encoding.push(e);
}
}
if (!(data instanceof Uint8Array)) {
throw new Error('The encoding must result in a Uint8Array. Fix your encoding chain.');
}
return {
encoding: encoding,
data: data
};
};
return Encoder;
}());
Binary.Encoder = Encoder;
(function (Encoder) {
var _a, _b;
function by(f) {
return new Encoder([f]);
}
Encoder.by = by;
function uint8(data) {
return {
encodings: [{ kind: 'ByteArray', type: 4 /* Uint8 */ }],
data: data
};
}
function int8(data) {
return {
encodings: [{ kind: 'ByteArray', type: 1 /* Int8 */ }],
data: new Uint8Array(data.buffer, data.byteOffset)
};
}
var writers = (_a = {},
_a[2 /* Int16 */] = function (v, i, a) { v.setInt16(2 * i, a, true); },
_a[5 /* Uint16 */] = function (v, i, a) { v.setUint16(2 * i, a, true); },
_a[3 /* Int32 */] = function (v, i, a) { v.setInt32(4 * i, a, true); },
_a[6 /* Uint32 */] = function (v, i, a) { v.setUint32(4 * i, a, true); },
_a[32 /* Float32 */] = function (v, i, a) { v.setFloat32(4 * i, a, true); },
_a[33 /* Float64 */] = function (v, i, a) { v.setFloat64(8 * i, a, true); },
_a);
var byteSizes = (_b = {},
_b[2 /* Int16 */] = 2,
_b[5 /* Uint16 */] = 2,
_b[3 /* Int32 */] = 4,
_b[6 /* Uint32 */] = 4,
_b[32 /* Float32 */] = 4,
_b[33 /* Float64 */] = 8,
_b);
function byteArray(data) {
var type = Binary.Encoding.getDataType(data);
if (type === 1 /* Int8 */)
return int8(data);
else if (type === 4 /* Uint8 */)
return uint8(data);
var result = new Uint8Array(data.length * byteSizes[type]);
var w = writers[type];
var view = new DataView(result.buffer);
for (var i = 0, n = data.length; i < n; i++) {
w(view, i, data[i]);
}
return {
encodings: [{ kind: 'ByteArray', type: type }],
data: result
};
}
Encoder.byteArray = byteArray;
function _fixedPoint(data, factor) {
var srcType = Binary.Encoding.getDataType(data);
var result = new Int32Array(data.length);
for (var i = 0, n = data.length; i < n; i++) {
result[i] = Math.round(data[i] * factor);
}
return {
encodings: [{ kind: 'FixedPoint', factor: factor, srcType: srcType }],
data: result
};
}
function fixedPoint(factor) { return function (data) { return _fixedPoint(data, factor); }; }
Encoder.fixedPoint = fixedPoint;
function _intervalQuantizaiton(data, min, max, numSteps, arrayType) {
var srcType = Binary.Encoding.getDataType(data);
if (!data.length) {
return {
encodings: [{ kind: 'IntervalQuantization', min: min, max: max, numSteps: numSteps, srcType: srcType }],
data: new Int32Array(0)
};
}
if (max < min) {
var t = min;
min = max;
max = t;
}
var delta = (max - min) / (numSteps - 1);
var output = new arrayType(data.length);
for (var i = 0, n = data.length; i < n; i++) {
var v = data[i];
if (v <= min)
output[i] = 0;
else if (v >= max)
output[i] = numSteps;
else
output[i] = (Math.round((v - min) / delta)) | 0;
}
return {
encodings: [{ kind: 'IntervalQuantization', min: min, max: max, numSteps: numSteps, srcType: srcType }],
data: output
};
}
function intervalQuantizaiton(min, max, numSteps, arrayType) {
if (arrayType === void 0) { arrayType = Int32Array; }
return function (data) { return _intervalQuantizaiton(data, min, max, numSteps, arrayType); };
}
Encoder.intervalQuantizaiton = intervalQuantizaiton;
function runLength(data) {
var srcType = Binary.Encoding.getDataType(data);
if (srcType === void 0) {
data = new Int32Array(data);
srcType = 3 /* Int32 */;
}
if (!data.length) {
return {
encodings: [{ kind: 'RunLength', srcType: srcType, srcSize: 0 }],
data: new Int32Array(0)
};
}
// calculate output size
var fullLength = 2;
for (var i = 1, il = data.length; i < il; i++) {
if (data[i - 1] !== data[i]) {
fullLength += 2;
}
}
var output = new Int32Array(fullLength);
var offset = 0;
var runLength = 1;
for (var i = 1, il = data.length; i < il; i++) {
if (data[i - 1] !== data[i]) {
output[offset] = data[i - 1];
output[offset + 1] = runLength;
runLength = 1;
offset += 2;
}
else {
++runLength;
}
}
output[offset] = data[data.length - 1];
output[offset + 1] = runLength;
return {
encodings: [{ kind: 'RunLength', srcType: srcType, srcSize: data.length }],
data: output
};
}
Encoder.runLength = runLength;
function delta(data) {
if (!Binary.Encoding.isSignedIntegerDataType(data)) {
throw new Error('Only signed integer types can be encoded using delta encoding.');
}
var srcType = Binary.Encoding.getDataType(data);
if (srcType === void 0) {
data = new Int32Array(data);
srcType = 3 /* Int32 */;
}
if (!data.length) {
return {
encodings: [{ kind: 'Delta', origin: 0, srcType: srcType }],
data: new data.constructor(0)
};
}
var output = new data.constructor(data.length);
var origin = data[0];
output[0] = data[0];
for (var i = 1, n = data.length; i < n; i++) {
output[i] = data[i] - data[i - 1];
}
output[0] = 0;
return {
encodings: [{ kind: 'Delta', origin: origin, srcType: srcType }],
data: output
};
}
Encoder.delta = delta;
function isSigned(data) {
for (var i = 0, n = data.length; i < n; i++) {
if (data[i] < 0)
return true;
}
return false;
}
function packingSize(data, upperLimit) {
var lowerLimit = -upperLimit - 1;
var size = 0;
for (var i = 0, n = data.length; i < n; i++) {
var value = data[i];
if (value === 0) {
size += 1;
}
else if (value > 0) {
size += Math.ceil(value / upperLimit);
if (value % upperLimit === 0)
size += 1;
}
else {
size += Math.ceil(value / lowerLimit);
if (value % lowerLimit === 0)
size += 1;
}
}
return size;
}
function determinePacking(data) {
var signed = isSigned(data);
var size8 = signed ? packingSize(data, 0x7F) : packingSize(data, 0xFF);
var size16 = signed ? packingSize(data, 0x7FFF) : packingSize(data, 0xFFFF);
if (data.length * 4 < size16 * 2) {
// 4 byte packing is the most effective
return {
isSigned: signed,
size: data.length,
bytesPerElement: 4
};
}
else if (size16 * 2 < size8) {
// 2 byte packing is the most effective
return {
isSigned: signed,
size: size16,
bytesPerElement: 2
};
}
else {
// 1 byte packing is the most effective
return {
isSigned: signed,
size: size8,
bytesPerElement: 1
};
}
;
}
function _integerPacking(data, packing) {
var upperLimit = packing.isSigned
? (packing.bytesPerElement === 1 ? 0x7F : 0x7FFF)
: (packing.bytesPerElement === 1 ? 0xFF : 0xFFFF);
var lowerLimit = -upperLimit - 1;
var n = data.length;
var packed = packing.isSigned
? packing.bytesPerElement === 1 ? new Int8Array(packing.size) : new Int16Array(packing.size)
: packing.bytesPerElement === 1 ? new Uint8Array(packing.size) : new Uint16Array(packing.size);
var j = 0;
for (var i = 0; i < n; i++) {
var value = data[i];
if (value >= 0) {
while (value >= upperLimit) {
packed[j] = upperLimit;
++j;
value -= upperLimit;
}
}
else {
while (value <= lowerLimit) {
packed[j] = lowerLimit;
++j;
value -= lowerLimit;
}
}
packed[j] = value;
++j;
}
var result = byteArray(packed);
return {
encodings: [{
kind: 'IntegerPacking',
byteCount: packing.bytesPerElement,
isUnsigned: !packing.isSigned,
srcSize: n
},
result.encodings[0]
],
data: result.data
};
}
/**
* Packs Int32 array. The packing level is determined automatically to either 1-, 2-, or 4-byte words.
*/
function integerPacking(data) {
if (!(data instanceof Int32Array)) {
throw new Error('Integer packing can only be applied to Int32 data.');
}
var packing = determinePacking(data);
if (packing.bytesPerElement === 4) {
// no packing done, Int32 encoding will be used
return byteArray(data);
}
return _integerPacking(data, packing);
}
Encoder.integerPacking = integerPacking;
function stringArray(data) {
var map = Object.create(null);
var strings = [];
var accLength = 0;
var offsets = CIFTools.Utils.ChunkedArray.create(function (s) { return new Int32Array(s); }, 1024, 1);
var output = new Int32Array(data.length);
CIFTools.Utils.ChunkedArray.add(offsets, 0);
var i = 0;
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
var s = data_1[_i];
// handle null strings.
if (s === null || s === void 0) {
output[i++] = -1;
continue;
}
var index = map[s];
if (index === void 0) {
// increment the length
accLength += s.length;
// store the string and index
index = strings.length;
strings[index] = s;
map[s] = index;
// write the offset
CIFTools.Utils.ChunkedArray.add(offsets, accLength);
}
output[i++] = index;
}
var encOffsets = Encoder.by(delta).and(integerPacking).encode(CIFTools.Utils.ChunkedArray.compact(offsets));
var encOutput = Encoder.by(delta).and(runLength).and(integerPacking).encode(output);
return {
encodings: [{ kind: 'StringArray', dataEncoding: encOutput.encoding, stringData: strings.join(''), offsetEncoding: encOffsets.encoding, offsets: encOffsets.data }],
data: encOutput.data
};
}
Encoder.stringArray = stringArray;
})(Encoder = Binary.Encoder || (Binary.Encoder = {}));
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
"use strict";
Binary.VERSION = '0.3.0';
var Encoding;
(function (Encoding) {
function getDataType(data) {
var srcType;
if (data instanceof Int8Array)
srcType = 1 /* Int8 */;
else if (data instanceof Int16Array)
srcType = 2 /* Int16 */;
else if (data instanceof Int32Array)
srcType = 3 /* Int32 */;
else if (data instanceof Uint8Array)
srcType = 4 /* Uint8 */;
else if (data instanceof Uint16Array)
srcType = 5 /* Uint16 */;
else if (data instanceof Uint32Array)
srcType = 6 /* Uint32 */;
else if (data instanceof Float32Array)
srcType = 32 /* Float32 */;
else if (data instanceof Float64Array)
srcType = 33 /* Float64 */;
else
throw new Error('Unsupported integer data type.');
return srcType;
}
Encoding.getDataType = getDataType;
function isSignedIntegerDataType(data) {
return data instanceof Int8Array || data instanceof Int16Array || data instanceof Int32Array;
}
Encoding.isSignedIntegerDataType = isSignedIntegerDataType;
})(Encoding = Binary.Encoding || (Binary.Encoding = {}));
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
"use strict";
function checkVersions(min, current) {
for (var i = 0; i < 2; i++) {
if (min[i] > current[i])
return false;
}
return true;
}
function parse(data) {
var minVersion = [0, 3];
try {
var array = new Uint8Array(data);
var unpacked = Binary.MessagePack.decode(array);
if (!checkVersions(minVersion, unpacked.version.match(/(\d)\.(\d)\.\d/).slice(1))) {
return CIFTools.ParserResult.error("Unsupported format version. Current " + unpacked.version + ", required " + minVersion.join('.') + ".");
}
var file = new Binary.File(unpacked);
return CIFTools.ParserResult.success(file);
}
catch (e) {
return CIFTools.ParserResult.error('' + e);
}
}
Binary.parse = parse;
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
// })(CIFTools || (CIFTools = {}));
/*
* Copyright (c) 2016 - now David Sehnal, licensed under MIT License, See LICENSE file for more info.
*/
// var CIFTools;
// (function (CIFTools) {
// var Binary;
(function (Binary) {
"use strict";
function encodeField(field, data, totalCount) {
var array, isNative = false;
if (field.typedArray) {
array = new field.typedArray(totalCount);
}
else {
isNative = true;
array = new Array(totalCount);
}
var mask = new Uint8Array(totalCount);
var presence = field.presence;
var getter = field.number ? field.number : field.string;
var allPresent = true;
var offset = 0;
for (var _i = 0, data_2 = data; _i < data_2.length; _i++) {
var _d = data_2[_i];
var d = _d.data;
for (var i = 0, _b = _d.count; i < _b; i++) {
var p = presence ? presence(d, i) : 0 /* Present */;
if (p !== 0 /* Present */) {
mask[offset] = p;
if (isNative)
array[offset] = null;
allPresent = false;
}
else {
mask[offset] = 0 /* Present */;
array[offset] = getter(d, i);
}
offset++;
}
}
var encoder = field.encoder ? field.encoder : Binary.Encoder.by(Binary.Encoder.stringArray);
var encoded = encoder.encode(array);
var maskData = void 0;
if (!allPresent) {
var maskRLE = Binary.Encoder.by(Binary.Encoder.runLength).and(Binary.Encoder.byteArray).encode(mask);
if (maskRLE.data.length < mask.length) {
maskData = maskRLE;
}
else {
maskData = Binary.Encoder.by(Binary.Encoder.byteArray).encode(mask);
}
}
return {
name: field.name,
data: encoded,
mask: maskData
};
}
var Writer = /** @class */ (function () {
function Writer(encoder) {
this.dataBlocks = [];
this.data = {
encoder: encoder,
version: Binary.VERSION,
dataBlocks: this.dataBlocks
};
}
Writer.prototype.startDataBlock = function (header) {
this.dataBlocks.push({
header: (header || '').replace(/[ \n\t]/g, '').toUpperCase(),
categories: []
});
};
Writer.prototype.writeCategory = function (category, contexts) {
if (!this.data) {
throw new Error('The writer contents have already been encoded, no more writing.');
}
if (!this.dataBlocks.length) {
throw new Error('No data block created.');
}
var src = !contexts || !contexts.length ? [category(void 0)] : contexts.map(function (c) { return category(c); });
var categories = src.filter(function (c) { return c && c.count > 0; });
if (!categories.length)
return;
var count = categories.reduce(function (a, c) { return a + c.count; }, 0);
if (!count)
return;
var first = categories[0];
var cat = { name: first.desc.name, columns: [], rowCount: count };
var data = categories.map(function (c) { return ({ data: c.data, count: c.count }); });
for (var _i = 0, _a = first.desc.fields; _i < _a.length; _i++) {
var f = _a[_i];
cat.columns.push(encodeField(f, data, count));
}
this.dataBlocks[this.dataBlocks.length - 1].categories.push(cat);
};
Writer.prototype.encode = function () {
this.encodedData = Binary.MessagePack.encode(this.data);
this.data = null;
this.dataBlocks = null;
};
Writer.prototype.flush = function (stream) {
stream.writeBinary(this.encodedData);
};
return Writer;
}());
Binary.Writer = Writer;
})(Binary = CIFTools.Binary || (CIFTools.Binary = {}));
})(CIFTools || (CIFTools = {}));
// return CIFTools;
// }
// if (typeof module === 'object' && typeof module.exports === 'object') {
// module.exports = __CIFTools();
// } else if (typeof define === 'function' && define.amd) {
// define(['require'], function(require) { return __CIFTools(); })
// } else {
// var __target = !!window ? window : this;
// __target.CIFTools = __CIFTools();
// }
/*
* ==========================================================
* COLOR PICKER PLUGIN 1.3.9
* ==========================================================
* Author: Taufik Nurrohman
* License: MIT
* ----------------------------------------------------------
*/
(function(win, doc, NS) {
var instance = '__instance__',
first = 'firstChild',
delay = setTimeout;
function is_set(x) {
return typeof x !== "undefined";
}
function is_string(x) {
return typeof x === "string";
}
function is_object(x) {
return typeof x === "object";
}
function object_length(x) {
return Object.keys(x).length;
}
function edge(a, b, c) {
if (a < b) return b;
if (a > c) return c;
return a;
}
function num(i, j) {
return parseInt(i, j || 10);
}
function round(i) {
return Math.round(i);
}
// [h, s, v] ... 0 <= h, s, v <= 1
function HSV2RGB(a) {
var h = +a[0],
s = +a[1],
v = +a[2],
r, g, b, i, f, p, q, t;
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
i = i || 0;
q = q || 0;
t = t || 0;
switch (i % 6) {
case 0:
r = v, g = t, b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
case 5:
r = v, g = p, b = q;
break;
}
return [round(r * 255), round(g * 255), round(b * 255)];
}
function HSV2HEX(a) {
return RGB2HEX(HSV2RGB(a));
}
// [r, g, b] ... 0 <= r, g, b <= 255
function RGB2HSV(a) {
var r = +a[0],
g = +a[1],
b = +a[2],
max = Math.max(r, g, b),
min = Math.min(r, g, b),
d = max - min,
h, s = (max === 0 ? 0 : d / max),
v = max / 255;
switch (max) {
case min:
h = 0;
break;
case r:
h = (g - b) + d * (g < b ? 6 : 0);
h /= 6 * d;
break;
case g:
h = (b - r) + d * 2;
h /= 6 * d;
break;
case b:
h = (r - g) + d * 4;
h /= 6 * d;
break;
}
return [h, s, v];
}
function RGB2HEX(a) {
var s = +a[2] | (+a[1] << 8) | (+a[0] << 16);
s = '000000' + s.toString(16);
return s.slice(-6);
}
// rrggbb or rgb
function HEX2HSV(s) {
return RGB2HSV(HEX2RGB(s));
}
function HEX2RGB(s) {
if (s.length === 3) {
s = s.replace(/./g, '$&$&');
}
return [num(s[0] + s[1], 16), num(s[2] + s[3], 16), num(s[4] + s[5], 16)];
}
// convert range from `0` to `360` and `0` to `100` in color into range from `0` to `1`
function _2HSV_pri(a) {
return [+a[0] / 360, +a[1] / 100, +a[2] / 100];
}
// convert range from `0` to `1` into `0` to `360` and `0` to `100` in color
function _2HSV_pub(a) {
return [round(+a[0] * 360), round(+a[1] * 100), round(+a[2] * 100)];
}
// convert range from `0` to `255` in color into range from `0` to `1`
function _2RGB_pri(a) {
return [+a[0] / 255, +a[1] / 255, +a[2] / 255];
}
// *
function parse(x) {
if (is_object(x)) return x;
var rgb = /\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/i.exec(x),
hsv = /\s*hsv\s*\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)\s*$/i.exec(x),
hex = x[0] === '#' && x.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
if (hex) {
return HEX2HSV(x.slice(1));
} else if (hsv) {
return _2HSV_pri([+hsv[1], +hsv[2], +hsv[3]]);
} else if (rgb) {
return RGB2HSV([+rgb[1], +rgb[2], +rgb[3]]);
}
return [0, 1, 1]; // default is red
}
(function($) {
// plugin version
$.version = '1.3.9';
// collect all instance(s)
$[instance] = {};
// plug to all instance(s)
$.each = function(fn, t) {
return delay(function() {
var ins = $[instance], i;
for (i in ins) {
fn(ins[i], i, ins);
}
}, t === 0 ? 0 : (t || 1)), $;
};
// static method(s)
$.parse = parse;
$._HSV2RGB = HSV2RGB;
$._HSV2HEX = HSV2HEX;
$._RGB2HSV = RGB2HSV;
$._HEX2HSV = HEX2HSV;
$._HEX2RGB = function(a) {
return _2RGB_pri(HEX2RGB(a));
};
$.HSV2RGB = function(a) {
return HSV2RGB(_2HSV_pri(a));
};
$.HSV2HEX = function(a) {
return HSV2HEX(_2HSV_pri(a));
};
$.RGB2HSV = function(a) {
return _2HSV_pub(RGB2HSV(a));
};
$.RGB2HEX = RGB2HEX;
$.HEX2HSV = function(s) {
return _2HSV_pub(HEX2HSV(s));
};
$.HEX2RGB = HEX2RGB;
})(win[NS] = function(target, events, parent) {
var b = doc.body,
h = doc.documentElement,
$ = this,
$$ = win[NS],
_ = false,
hooks = {},
picker = doc.createElement('div'),
on_down = "touchstart mousedown",
on_move = "touchmove mousemove",
on_up = "touchend mouseup",
on_resize = "orientationchange resize";
// return a new instance if `CP` was called without the `new` operator
if (!($ instanceof $$)) {
return new $$(target, events);
}
// store color picker instance to `CP.__instance__`
$$[instance][target.id || target.name || object_length($$[instance])] = $;
// trigger color picker panel on click by default
if (!is_set(events) || events === true) {
events = on_down;
}
// add event
function on(ev, el, fn) {
ev = ev.split(/\s+/);
for (var i = 0, ien = ev.length; i < ien; ++i) {
el.addEventListener(ev[i], fn, false);
}
}
// remove event
function off(ev, el, fn) {
ev = ev.split(/\s+/);
for (var i = 0, ien = ev.length; i < ien; ++i) {
el.removeEventListener(ev[i], fn);
}
}
// get mouse/finger coordinate
function point(el, e) {
var T = 'touches',
X = 'clientX',
Y = 'clientY',
x = !!e[T] ? e[T][0][X] : e[X],
y = !!e[T] ? e[T][0][Y] : e[Y],
o = offset(el);
return {
x: x - o.l,
y: y - o.t
};
}
// get position
function offset(el) {
var left, top, rect;
if (el === win) {
left = win.pageXOffset || h.scrollLeft;
top = win.pageYOffset || h.scrollTop;
} else {
rect = el.getBoundingClientRect();
left = rect.left;
top = rect.top;
}
return {
l: left,
t: top
};
}
// get closest parent
function closest(a, b) {
while ((a = a.parentElement) && a !== b);
return a;
}
// prevent default
function prevent(e) {
if (e) e.preventDefault();
}
// get dimension
function size(el) {
return el === win ? {
w: win.innerWidth,
h: win.innerHeight
} : {
w: el.offsetWidth,
h: el.offsetHeight
};
}
// get color data
function get_data(a) {
return _ || (is_set(a) ? a : false);
}
// set color data
function set_data(a) {
_ = a;
}
// add hook
function add(ev, fn, id) {
if (!is_set(ev)) return hooks;
if (!is_set(fn)) return hooks[ev];
if (!is_set(hooks[ev])) hooks[ev] = {};
if (!is_set(id)) id = object_length(hooks[ev]);
return hooks[ev][id] = fn, $;
}
// remove hook
function remove(ev, id) {
if (!is_set(ev)) return hooks = {}, $;
if (!is_set(id)) return hooks[ev] = {}, $;
return delete hooks[ev][id], $;
}
// trigger hook
function trigger(ev, a, id) {
if (!is_set(hooks[ev])) return $;
if (!is_set(id)) {
for (var i in hooks[ev]) {
hooks[ev][i].apply($, a);
}
} else {
if (is_set(hooks[ev][id])) {
hooks[ev][id].apply($, a);
}
}
return $;
}
// initialize data ...
set_data($$.parse(target.getAttribute('data-color') || target.value || [0, 1, 1]));
// generate color picker pane ...
picker.className = 'color-picker';
picker.innerHTML = '
';
var c = picker[first].children,
HSV = get_data([0, 1, 1]), // default is red
H = c[0],
SV = c[1],
H_point = H[first],
SV_point = SV[first],
start_H = 0,
start_SV = 0,
drag_H = 0,
drag_SV = 0,
left = 0,
top = 0,
P_W = 0,
P_H = 0,
v = HSV2HEX(HSV),
set;
// on update ...
function trigger_(k, x) {
if (!k || k === "h") {
trigger("change:h", x);
}
if (!k || k === "sv") {
trigger("change:sv", x);
}
trigger("change", x);
}
// is visible?
function visible() {
return picker.parentNode;
}
// create
function create(first, bucket) {
if (!first) {
(parent || bucket || b).appendChild(picker), $.visible = true;
}
P_W = size(picker).w;
P_H = size(picker).h;
var SV_size = size(SV),
SV_point_size = size(SV_point),
H_H = size(H).h,
SV_W = SV_size.w,
SV_H = SV_size.h,
H_point_H = size(H_point).h,
SV_point_W = SV_point_size.w,
SV_point_H = SV_point_size.h;
if (first) {
picker.style.left = picker.style.top = '-9999px';
function click(e) {
var t = e.target,
is_target = t === target || closest(t, target) === target;
if (is_target) {
create();
} else {
$.exit();
}
trigger(is_target ? "enter" : "exit", [$]);
}
if (events !== false) {
on(events, target, click);
}
$.create = function() {
return create(1), trigger("create", [$]), $;
};
$.destroy = function() {
if (events !== false) {
off(events, target, click);
}
$.exit(), set_data(false);
return trigger("destroy", [$]), $;
};
} else {
fit();
}
set = function() {
HSV = get_data(HSV), color();
H_point.style.top = (H_H - (H_point_H / 2) - (H_H * +HSV[0])) + 'px';
SV_point.style.right = (SV_W - (SV_point_W / 2) - (SV_W * +HSV[1])) + 'px';
SV_point.style.top = (SV_H - (SV_point_H / 2) - (SV_H * +HSV[2])) + 'px';
};
$.exit = function(e) {
if (visible()) {
visible().removeChild(picker);
$.visible = false;
}
off(on_down, H, down_H);
off(on_down, SV, down_SV);
off(on_move, doc, move);
off(on_up, doc, stop);
off(on_resize, win, fit);
return $;
};
function color(e) {
var a = HSV2RGB(HSV),
b = HSV2RGB([HSV[0], 1, 1]);
SV.style.backgroundColor = 'rgb(' + b.join(',') + ')';
set_data(HSV);
prevent(e);
};
set();
function do_H(e) {
var y = edge(point(H, e).y, 0, H_H);
HSV[0] = (H_H - y) / H_H;
H_point.style.top = (y - (H_point_H / 2)) + 'px';
color(e);
}
function do_SV(e) {
var o = point(SV, e),
x = edge(o.x, 0, SV_W),
y = edge(o.y, 0, SV_H);
HSV[1] = 1 - ((SV_W - x) / SV_W);
HSV[2] = (SV_H - y) / SV_H;
SV_point.style.right = (SV_W - x - (SV_point_W / 2)) + 'px';
SV_point.style.top = (y - (SV_point_H / 2)) + 'px';
color(e);
}
function move(e) {
if (drag_H) {
do_H(e), v = HSV2HEX(HSV);
if (!start_H) {
trigger("drag:h", [v, $]);
trigger("drag", [v, $]);
trigger_("h", [v, $]);
}
}
if (drag_SV) {
do_SV(e), v = HSV2HEX(HSV);
if (!start_SV) {
trigger("drag:sv", [v, $]);
trigger("drag", [v, $]);
trigger_("sv", [v, $]);
}
}
start_H = 0,
start_SV = 0;
}
function stop(e) {
var t = e.target,
k = drag_H ? "h" : "sv",
a = [HSV2HEX(HSV), $],
is_target = t === target || closest(t, target) === target,
is_picker = t === picker || closest(t, picker) === picker;
if (!is_target && !is_picker) {
// click outside the target or picker element to exit
if (visible() && events !== false) $.exit(), trigger("exit", [$]), trigger_(0, a);
} else {
if (is_picker) {
trigger("stop:" + k, a);
trigger("stop", a);
trigger_(k, a);
}
}
drag_H = 0,
drag_SV = 0;
}
function down_H(e) {
start_H = 1,
drag_H = 1,
move(e), prevent(e);
trigger("start:h", [v, $]);
trigger("start", [v, $]);
trigger_("h", [v, $]);
}
function down_SV(e) {
start_SV = 1,
drag_SV = 1,
move(e), prevent(e);
trigger("start:sv", [v, $]);
trigger("start", [v, $]);
trigger_("sv", [v, $]);
}
if (!first) {
on(on_down, H, down_H);
on(on_down, SV, down_SV);
on(on_move, doc, move);
on(on_up, doc, stop);
on(on_resize, win, fit);
}
} create(1);
delay(function() {
var a = [HSV2HEX(HSV), $];
trigger("create", a);
trigger_(0, a);
}, 0);
// fit to window
$.fit = function(o) {
var w = size(win),
y = size(h),
screen_w = w.w - y.w, // vertical scroll bar
screen_h = w.h - h.clientHeight, // horizontal scroll bar
ww = offset(win),
to = offset(target);
left = to.l + ww.l;
top = to.t + ww.t + size(target).h; // drop!
if (is_object(o)) {
is_set(o[0]) && (left = o[0]);
is_set(o[1]) && (top = o[1]);
} else {
var min_x = ww.l,
min_y = ww.t,
max_x = ww.l + w.w - P_W - screen_w,
max_y = ww.t + w.h - P_H - screen_h;
left = edge(left, min_x, max_x) >> 0;
top = edge(top, min_y, max_y) >> 0;
}
picker.style.left = left + 'px';
picker.style.top = top + 'px';
return trigger("fit", [$]), $;
};
// for event listener ID
function fit() {
return $.fit();
}
// set hidden color picker data
$.set = function(a) {
if (!is_set(a)) return get_data();
if (is_string(a)) {
a = $$.parse(a);
}
return set_data(a), set(), $;
};
// alias for `$.set()`
$.get = function(a) {
return get_data(a);
};
// register to global ...
$.target = target;
$.picker = picker;
$.visible = false;
$.on = add;
$.off = remove;
$.fire = trigger;
$.hooks = hooks;
$.enter = function(bucket) {
return create(0, bucket);
};
// return the global object
return $;
});
})(window, document, 'CP');
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 1.3.8
* 2018-03-22 14:03:47
*
* By Eli Grey, https://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/* @source http://purl.eligrey.com/github/FileSaver.js/blob/master/src/FileSaver.js */
//var saveAs = saveAs || (function(view) {
var saveAs = (function(view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
, get_URL = function() {
return view.URL || view.webkitURL || view;
}
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = "download" in save_link
, click = function(node) {
var event = new MouseEvent("click");
node.dispatchEvent(event);
}
, is_safari = /constructor/i.test(view.HTMLElement) || view.safari
, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
, setImmediate = view.setImmediate || view.setTimeout
, throw_outside = function(ex) {
setImmediate(function() {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
, arbitrary_revoke_timeout = 1000 * 40 // in ms
, revoke = function(file) {
var revoker = function() {
if (typeof file === "string") { // file is an object URL
get_URL().revokeObjectURL(file);
} else { // file is a File
file.remove();
}
};
setTimeout(revoker, arbitrary_revoke_timeout);
}
, dispatch = function(filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, auto_bom = function(blob) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
//if (blob && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
}
return blob;
}
, FileSaver = function(blob, name, no_auto_bom) {
if (!no_auto_bom) {
blob = auto_bom(blob);
}
// First try a.download, then web filesystem, then object URLs
var
filesaver = this
, type = (blob) ? blob.type : undefined
, force = type === force_saveable_type
, object_url
, dispatch_all = function() {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function() {
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader();
reader.onloadend = function() {
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
var urlTarget = '_blank';
var popup = view.open(url, urlTarget);
if(!popup) view.location.href = url;
url=undefined; // release reference before dispatching
filesaver.readyState = filesaver.DONE;
dispatch_all();
};
reader.readAsDataURL(blob);
filesaver.readyState = filesaver.INIT;
return;
}
// don't create more object URLs than needed
if (!object_url) object_url = get_URL().createObjectURL(blob);
if (force) {
view.location.href = object_url;
} else {
var opened = view.open(object_url, "_blank");
if (!opened) {
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
view.location.href = object_url;
}
}
filesaver.readyState = filesaver.DONE;
dispatch_all();
revoke(object_url);
}
;
filesaver.readyState = filesaver.INIT;
if (can_use_save_link) {
if (!object_url) object_url = get_URL().createObjectURL(blob);
setImmediate(function() {
save_link.href = object_url;
save_link.download = name;
click(save_link);
dispatch_all();
revoke(object_url);
filesaver.readyState = filesaver.DONE;
}, 0);
return;
}
fs_error();
}
, FS_proto = FileSaver.prototype
, saveAs = function(blob, name, no_auto_bom) {
return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
}
;
// IE 10+ (native saveAs)
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
return function(blob, name, no_auto_bom) {
name = name || blob.name || "download";
if (!no_auto_bom) {
blob = auto_bom(blob);
}
return navigator.msSaveOrOpenBlob(blob, name);
};
}
// todo: detect chrome extensions & packaged apps
//save_link.target = "_blank";
FS_proto.abort = function(){};
FS_proto.readyState = FS_proto.INIT = 0;
FS_proto.WRITING = 1;
FS_proto.DONE = 2;
FS_proto.error =
FS_proto.onwritestart =
FS_proto.onprogress =
FS_proto.onwrite =
FS_proto.onabort =
FS_proto.onerror =
FS_proto.onwriteend =
null;
return saveAs;
}(
typeof self !== "undefined" && self
|| typeof window !== "undefined" && window
|| this
));
/*
* JavaScript Canvas to Blob
* https://github.com/blueimp/JavaScript-Canvas-to-Blob
*
* Copyright 2012, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* https://opensource.org/licenses/MIT
*
* Based on stackoverflow user Stoive's code snippet:
* http://stackoverflow.com/q/4998908
*/
/* global atob, Blob, define */
;(function (window) {
'use strict';
var CanvasPrototype =
window.HTMLCanvasElement && window.HTMLCanvasElement.prototype
var hasBlobConstructor =
window.Blob &&
(function () {
try {
return Boolean(new Blob())
} catch (e) {
return false
}
})()
var hasArrayBufferViewSupport =
hasBlobConstructor &&
window.Uint8Array &&
(function () {
try {
return new Blob([new Uint8Array(100)]).size === 100
} catch (e) {
return false
}
})()
var BlobBuilder =
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder
var dataURIPattern = /^data:((.*?)(;charset=.*?)?)(;base64)?,/
var dataURLtoBlob =
(hasBlobConstructor || BlobBuilder) &&
window.atob &&
window.ArrayBuffer &&
window.Uint8Array &&
function (dataURI) {
var matches,
mediaType,
isBase64,
dataString,
byteString,
arrayBuffer,
intArray,
i,
bb
// Parse the dataURI components as per RFC 2397
matches = dataURI.match(dataURIPattern)
if (!matches) {
throw new Error('invalid data URI')
}
// Default to text/plain;charset=US-ASCII
mediaType = matches[2]
? matches[1]
: 'text/plain' + (matches[3] || ';charset=US-ASCII')
isBase64 = !!matches[4]
dataString = dataURI.slice(matches[0].length)
if (isBase64) {
// Convert base64 to raw binary data held in a string:
byteString = atob(dataString)
} else {
// Convert base64/URLEncoded data component to raw binary:
byteString = decodeURIComponent(dataString)
}
// Write the bytes of the string to an ArrayBuffer:
arrayBuffer = new ArrayBuffer(byteString.length)
intArray = new Uint8Array(arrayBuffer)
for (i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i)
}
// Write the ArrayBuffer (or ArrayBufferView) to a blob:
if (hasBlobConstructor) {
return new Blob([hasArrayBufferViewSupport ? intArray : arrayBuffer], {
type: mediaType
})
}
bb = new BlobBuilder()
bb.append(arrayBuffer)
return bb.getBlob(mediaType)
}
if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
if (CanvasPrototype.mozGetAsFile) {
CanvasPrototype.toBlob = function (callback, type, quality) {
var self = this
setTimeout(function () {
if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
callback(dataURLtoBlob(self.toDataURL(type, quality)))
} else {
callback(self.mozGetAsFile('blob', type))
}
})
}
} else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
CanvasPrototype.toBlob = function (callback, type, quality) {
var self = this
setTimeout(function () {
callback(dataURLtoBlob(self.toDataURL(type, quality)))
})
}
}
}
if (typeof define === 'function' && define.amd) {
define(function () {
return dataURLtoBlob
})
} else if (typeof module === 'object' && module.exports) {
module.exports = dataURLtoBlob
} else {
window.dataURLtoBlob = dataURLtoBlob
}
})(window)
var icn3d = (function (exports) {
'use strict';
/**
* @license
* Copyright 2010-2025 Three.js Authors
* SPDX-License-Identifier: MIT
*/
const REVISION = '177';
/**
* Disables face culling.
*
* @type {number}
* @constant
*/
const CullFaceNone = 0;
/**
* Culls back faces.
*
* @type {number}
* @constant
*/
const CullFaceBack = 1;
/**
* Culls front faces.
*
* @type {number}
* @constant
*/
const CullFaceFront = 2;
/**
* Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm.
*
* @type {number}
* @constant
*/
const PCFShadowMap = 1;
/**
* Filters shadow maps using the Percentage-Closer Filtering (PCF) algorithm with
* better soft shadows especially when using low-resolution shadow maps.
*
* @type {number}
* @constant
*/
const PCFSoftShadowMap = 2;
/**
* Filters shadow maps using the Variance Shadow Map (VSM) algorithm.
* When using VSMShadowMap all shadow receivers will also cast shadows.
*
* @type {number}
* @constant
*/
const VSMShadowMap = 3;
/**
* Only front faces are rendered.
*
* @type {number}
* @constant
*/
const FrontSide$1 = 0;
/**
* Only back faces are rendered.
*
* @type {number}
* @constant
*/
const BackSide = 1;
/**
* Both front and back faces are rendered.
*
* @type {number}
* @constant
*/
const DoubleSide$1 = 2;
/**
* No blending is performed which effectively disables
* alpha transparency.
*
* @type {number}
* @constant
*/
const NoBlending = 0;
/**
* The default blending.
*
* @type {number}
* @constant
*/
const NormalBlending = 1;
/**
* Represents additive blending.
*
* @type {number}
* @constant
*/
const AdditiveBlending = 2;
/**
* Represents subtractive blending.
*
* @type {number}
* @constant
*/
const SubtractiveBlending = 3;
/**
* Represents multiply blending.
*
* @type {number}
* @constant
*/
const MultiplyBlending = 4;
/**
* Represents custom blending.
*
* @type {number}
* @constant
*/
const CustomBlending = 5;
/**
* A `source + destination` blending equation.
*
* @type {number}
* @constant
*/
const AddEquation = 100;
/**
* A `source - destination` blending equation.
*
* @type {number}
* @constant
*/
const SubtractEquation = 101;
/**
* A `destination - source` blending equation.
*
* @type {number}
* @constant
*/
const ReverseSubtractEquation = 102;
/**
* A blend equation that uses the minimum of source and destination.
*
* @type {number}
* @constant
*/
const MinEquation = 103;
/**
* A blend equation that uses the maximum of source and destination.
*
* @type {number}
* @constant
*/
const MaxEquation = 104;
/**
* Multiplies all colors by `0`.
*
* @type {number}
* @constant
*/
const ZeroFactor = 200;
/**
* Multiplies all colors by `1`.
*
* @type {number}
* @constant
*/
const OneFactor = 201;
/**
* Multiplies all colors by the source colors.
*
* @type {number}
* @constant
*/
const SrcColorFactor = 202;
/**
* Multiplies all colors by `1` minus each source color.
*
* @type {number}
* @constant
*/
const OneMinusSrcColorFactor = 203;
/**
* Multiplies all colors by the source alpha value.
*
* @type {number}
* @constant
*/
const SrcAlphaFactor = 204;
/**
* Multiplies all colors by 1 minus the source alpha value.
*
* @type {number}
* @constant
*/
const OneMinusSrcAlphaFactor = 205;
/**
* Multiplies all colors by the destination alpha value.
*
* @type {number}
* @constant
*/
const DstAlphaFactor = 206;
/**
* Multiplies all colors by `1` minus the destination alpha value.
*
* @type {number}
* @constant
*/
const OneMinusDstAlphaFactor = 207;
/**
* Multiplies all colors by the destination color.
*
* @type {number}
* @constant
*/
const DstColorFactor = 208;
/**
* Multiplies all colors by `1` minus each destination color.
*
* @type {number}
* @constant
*/
const OneMinusDstColorFactor = 209;
/**
* Multiplies the RGB colors by the smaller of either the source alpha
* value or the value of `1` minus the destination alpha value. The alpha
* value is multiplied by `1`.
*
* @type {number}
* @constant
*/
const SrcAlphaSaturateFactor = 210;
/**
* Multiplies all colors by a constant color.
*
* @type {number}
* @constant
*/
const ConstantColorFactor = 211;
/**
* Multiplies all colors by `1` minus a constant color.
*
* @type {number}
* @constant
*/
const OneMinusConstantColorFactor = 212;
/**
* Multiplies all colors by a constant alpha value.
*
* @type {number}
* @constant
*/
const ConstantAlphaFactor = 213;
/**
* Multiplies all colors by 1 minus a constant alpha value.
*
* @type {number}
* @constant
*/
const OneMinusConstantAlphaFactor = 214;
/**
* Never pass.
*
* @type {number}
* @constant
*/
const NeverDepth = 0;
/**
* Always pass.
*
* @type {number}
* @constant
*/
const AlwaysDepth = 1;
/**
* Pass if the incoming value is less than the depth buffer value.
*
* @type {number}
* @constant
*/
const LessDepth = 2;
/**
* Pass if the incoming value is less than or equal to the depth buffer value.
*
* @type {number}
* @constant
*/
const LessEqualDepth = 3;
/**
* Pass if the incoming value equals the depth buffer value.
*
* @type {number}
* @constant
*/
const EqualDepth = 4;
/**
* Pass if the incoming value is greater than or equal to the depth buffer value.
*
* @type {number}
* @constant
*/
const GreaterEqualDepth = 5;
/**
* Pass if the incoming value is greater than the depth buffer value.
*
* @type {number}
* @constant
*/
const GreaterDepth = 6;
/**
* Pass if the incoming value is not equal to the depth buffer value.
*
* @type {number}
* @constant
*/
const NotEqualDepth = 7;
/**
* Multiplies the environment map color with the surface color.
*
* @type {number}
* @constant
*/
const MultiplyOperation = 0;
/**
* Uses reflectivity to blend between the two colors.
*
* @type {number}
* @constant
*/
const MixOperation = 1;
/**
* Adds the two colors.
*
* @type {number}
* @constant
*/
const AddOperation = 2;
/**
* No tone mapping is applied.
*
* @type {number}
* @constant
*/
const NoToneMapping = 0;
/**
* Linear tone mapping.
*
* @type {number}
* @constant
*/
const LinearToneMapping = 1;
/**
* Reinhard tone mapping.
*
* @type {number}
* @constant
*/
const ReinhardToneMapping = 2;
/**
* Cineon tone mapping.
*
* @type {number}
* @constant
*/
const CineonToneMapping = 3;
/**
* ACES Filmic tone mapping.
*
* @type {number}
* @constant
*/
const ACESFilmicToneMapping = 4;
/**
* Custom tone mapping.
*
* Expects a custom implementation by modifying shader code of the material's fragment shader.
*
* @type {number}
* @constant
*/
const CustomToneMapping = 5;
/**
* AgX tone mapping.
*
* @type {number}
* @constant
*/
const AgXToneMapping = 6;
/**
* Neutral tone mapping.
*
* Implementation based on the Khronos 3D Commerce Group standard tone mapping.
*
* @type {number}
* @constant
*/
const NeutralToneMapping = 7;
/**
* Maps textures using the geometry's UV coordinates.
*
* @type {number}
* @constant
*/
const UVMapping = 300;
/**
* Reflection mapping for cube textures.
*
* @type {number}
* @constant
*/
const CubeReflectionMapping = 301;
/**
* Refraction mapping for cube textures.
*
* @type {number}
* @constant
*/
const CubeRefractionMapping = 302;
/**
* Reflection mapping for equirectangular textures.
*
* @type {number}
* @constant
*/
const EquirectangularReflectionMapping = 303;
/**
* Refraction mapping for equirectangular textures.
*
* @type {number}
* @constant
*/
const EquirectangularRefractionMapping = 304;
/**
* Reflection mapping for PMREM textures.
*
* @type {number}
* @constant
*/
const CubeUVReflectionMapping = 306;
/**
* The texture will simply repeat to infinity.
*
* @type {number}
* @constant
*/
const RepeatWrapping$1 = 1000;
/**
* The last pixel of the texture stretches to the edge of the mesh.
*
* @type {number}
* @constant
*/
const ClampToEdgeWrapping = 1001;
/**
* The texture will repeats to infinity, mirroring on each repeat.
*
* @type {number}
* @constant
*/
const MirroredRepeatWrapping = 1002;
/**
* Returns the value of the texture element that is nearest (in Manhattan distance)
* to the specified texture coordinates.
*
* @type {number}
* @constant
*/
const NearestFilter = 1003;
/**
* Chooses the mipmap that most closely matches the size of the pixel being textured
* and uses the `NearestFilter` criterion (the texel nearest to the center of the pixel)
* to produce a texture value.
*
* @type {number}
* @constant
*/
const NearestMipmapNearestFilter = 1004;
/**
* Chooses the two mipmaps that most closely match the size of the pixel being textured and
* uses the `NearestFilter` criterion to produce a texture value from each mipmap.
* The final texture value is a weighted average of those two values.
*
* @type {number}
* @constant
*/
const NearestMipmapLinearFilter = 1005;
/**
* Returns the weighted average of the four texture elements that are closest to the specified
* texture coordinates, and can include items wrapped or repeated from other parts of a texture,
* depending on the values of `wrapS` and `wrapT`, and on the exact mapping.
*
* @type {number}
* @constant
*/
const LinearFilter$1 = 1006;
/**
* Chooses the mipmap that most closely matches the size of the pixel being textured and uses
* the `LinearFilter` criterion (a weighted average of the four texels that are closest to the
* center of the pixel) to produce a texture value.
*
* @type {number}
* @constant
*/
const LinearMipmapNearestFilter = 1007;
/**
* Chooses the two mipmaps that most closely match the size of the pixel being textured and uses
* the `LinearFilter` criterion to produce a texture value from each mipmap. The final texture value
* is a weighted average of those two values.
*
* @type {number}
* @constant
*/
const LinearMipmapLinearFilter$1 = 1008;
/**
* An unsigned byte data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedByteType = 1009;
/**
* A byte data type for textures.
*
* @type {number}
* @constant
*/
const ByteType = 1010;
/**
* A short data type for textures.
*
* @type {number}
* @constant
*/
const ShortType = 1011;
/**
* An unsigned short data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedShortType = 1012;
/**
* An int data type for textures.
*
* @type {number}
* @constant
*/
const IntType = 1013;
/**
* An unsigned int data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedIntType = 1014;
/**
* A float data type for textures.
*
* @type {number}
* @constant
*/
const FloatType = 1015;
/**
* A half float data type for textures.
*
* @type {number}
* @constant
*/
const HalfFloatType = 1016;
/**
* An unsigned short 4_4_4_4 (packed) data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedShort4444Type = 1017;
/**
* An unsigned short 5_5_5_1 (packed) data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedShort5551Type = 1018;
/**
* An unsigned int 24_8 data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedInt248Type = 1020;
/**
* An unsigned int 5_9_9_9 (packed) data type for textures.
*
* @type {number}
* @constant
*/
const UnsignedInt5999Type = 35902;
/**
* Discards the red, green and blue components and reads just the alpha component.
*
* @type {number}
* @constant
*/
const AlphaFormat = 1021;
/**
* Discards the alpha component and reads the red, green and blue component.
*
* @type {number}
* @constant
*/
const RGBFormat = 1022;
/**
* Reads the red, green, blue and alpha components.
*
* @type {number}
* @constant
*/
const RGBAFormat = 1023;
/**
* Reads each element as a single depth value, converts it to floating point, and clamps to the range `[0,1]`.
*
* @type {number}
* @constant
*/
const DepthFormat = 1026;
/**
* Reads each element is a pair of depth and stencil values. The depth component of the pair is interpreted as
* in `DepthFormat`. The stencil component is interpreted based on the depth + stencil internal format.
*
* @type {number}
* @constant
*/
const DepthStencilFormat = 1027;
/**
* Discards the green, blue and alpha components and reads just the red component.
*
* @type {number}
* @constant
*/
const RedFormat = 1028;
/**
* Discards the green, blue and alpha components and reads just the red component. The texels are read as integers instead of floating point.
*
* @type {number}
* @constant
*/
const RedIntegerFormat = 1029;
/**
* Discards the alpha, and blue components and reads the red, and green components.
*
* @type {number}
* @constant
*/
const RGFormat = 1030;
/**
* Discards the alpha, and blue components and reads the red, and green components. The texels are read as integers instead of floating point.
*
* @type {number}
* @constant
*/
const RGIntegerFormat = 1031;
/**
* Reads the red, green, blue and alpha components. The texels are read as integers instead of floating point.
*
* @type {number}
* @constant
*/
const RGBAIntegerFormat = 1033;
/**
* A DXT1-compressed image in an RGB image format.
*
* @type {number}
* @constant
*/
const RGB_S3TC_DXT1_Format = 33776;
/**
* A DXT1-compressed image in an RGB image format with a simple on/off alpha value.
*
* @type {number}
* @constant
*/
const RGBA_S3TC_DXT1_Format = 33777;
/**
* A DXT3-compressed image in an RGBA image format. Compared to a 32-bit RGBA texture, it offers 4:1 compression.
*
* @type {number}
* @constant
*/
const RGBA_S3TC_DXT3_Format = 33778;
/**
* A DXT5-compressed image in an RGBA image format. It also provides a 4:1 compression, but differs to the DXT3
* compression in how the alpha compression is done.
*
* @type {number}
* @constant
*/
const RGBA_S3TC_DXT5_Format = 33779;
/**
* PVRTC RGB compression in 4-bit mode. One block for each 4×4 pixels.
*
* @type {number}
* @constant
*/
const RGB_PVRTC_4BPPV1_Format = 35840;
/**
* PVRTC RGB compression in 2-bit mode. One block for each 8×4 pixels.
*
* @type {number}
* @constant
*/
const RGB_PVRTC_2BPPV1_Format = 35841;
/**
* PVRTC RGBA compression in 4-bit mode. One block for each 4×4 pixels.
*
* @type {number}
* @constant
*/
const RGBA_PVRTC_4BPPV1_Format = 35842;
/**
* PVRTC RGBA compression in 2-bit mode. One block for each 8×4 pixels.
*
* @type {number}
* @constant
*/
const RGBA_PVRTC_2BPPV1_Format = 35843;
/**
* ETC1 RGB format.
*
* @type {number}
* @constant
*/
const RGB_ETC1_Format = 36196;
/**
* ETC2 RGB format.
*
* @type {number}
* @constant
*/
const RGB_ETC2_Format = 37492;
/**
* ETC2 RGBA format.
*
* @type {number}
* @constant
*/
const RGBA_ETC2_EAC_Format = 37496;
/**
* ASTC RGBA 4x4 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_4x4_Format = 37808;
/**
* ASTC RGBA 5x4 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_5x4_Format = 37809;
/**
* ASTC RGBA 5x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_5x5_Format = 37810;
/**
* ASTC RGBA 6x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_6x5_Format = 37811;
/**
* ASTC RGBA 6x6 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_6x6_Format = 37812;
/**
* ASTC RGBA 8x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_8x5_Format = 37813;
/**
* ASTC RGBA 8x6 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_8x6_Format = 37814;
/**
* ASTC RGBA 8x8 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_8x8_Format = 37815;
/**
* ASTC RGBA 10x5 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x5_Format = 37816;
/**
* ASTC RGBA 10x6 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x6_Format = 37817;
/**
* ASTC RGBA 10x8 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x8_Format = 37818;
/**
* ASTC RGBA 10x10 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_10x10_Format = 37819;
/**
* ASTC RGBA 12x10 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_12x10_Format = 37820;
/**
* ASTC RGBA 12x12 format.
*
* @type {number}
* @constant
*/
const RGBA_ASTC_12x12_Format = 37821;
/**
* BPTC RGBA format.
*
* @type {number}
* @constant
*/
const RGBA_BPTC_Format = 36492;
/**
* BPTC Signed RGB format.
*
* @type {number}
* @constant
*/
const RGB_BPTC_SIGNED_Format = 36494;
/**
* BPTC Unsigned RGB format.
*
* @type {number}
* @constant
*/
const RGB_BPTC_UNSIGNED_Format = 36495;
/**
* RGTC1 Red format.
*
* @type {number}
* @constant
*/
const RED_RGTC1_Format = 36283;
/**
* RGTC1 Signed Red format.
*
* @type {number}
* @constant
*/
const SIGNED_RED_RGTC1_Format = 36284;
/**
* RGTC2 Red Green format.
*
* @type {number}
* @constant
*/
const RED_GREEN_RGTC2_Format = 36285;
/**
* RGTC2 Signed Red Green format.
*
* @type {number}
* @constant
*/
const SIGNED_RED_GREEN_RGTC2_Format = 36286;
/**
* Discrete interpolation mode for keyframe tracks.
*
* @type {number}
* @constant
*/
const InterpolateDiscrete = 2300;
/**
* Linear interpolation mode for keyframe tracks.
*
* @type {number}
* @constant
*/
const InterpolateLinear$1 = 2301;
/**
* Basic depth packing.
*
* @type {number}
* @constant
*/
const BasicDepthPacking = 3200;
/**
* A depth value is packed into 32 bit RGBA.
*
* @type {number}
* @constant
*/
const RGBADepthPacking = 3201;
/**
* Normal information is relative to the underlying surface.
*
* @type {number}
* @constant
*/
const TangentSpaceNormalMap$1 = 0;
/**
* Normal information is relative to the object orientation.
*
* @type {number}
* @constant
*/
const ObjectSpaceNormalMap = 1;
// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available.
/**
* No color space.
*
* @type {string}
* @constant
*/
const NoColorSpace = '';
/**
* sRGB color space.
*
* @type {string}
* @constant
*/
const SRGBColorSpace = 'srgb';
/**
* sRGB-linear color space.
*
* @type {string}
* @constant
*/
const LinearSRGBColorSpace = 'srgb-linear';
/**
* Linear transfer function.
*
* @type {string}
* @constant
*/
const LinearTransfer = 'linear';
/**
* sRGB transfer function.
*
* @type {string}
* @constant
*/
const SRGBTransfer = 'srgb';
/**
* Keeps the current value.
*
* @type {number}
* @constant
*/
const KeepStencilOp = 7680;
/**
* Will always return true.
*
* @type {number}
* @constant
*/
const AlwaysStencilFunc = 519;
/**
* Never pass.
*
* @type {number}
* @constant
*/
const NeverCompare = 512;
/**
* Pass if the incoming value is less than the texture value.
*
* @type {number}
* @constant
*/
const LessCompare = 513;
/**
* Pass if the incoming value equals the texture value.
*
* @type {number}
* @constant
*/
const EqualCompare = 514;
/**
* Pass if the incoming value is less than or equal to the texture value.
*
* @type {number}
* @constant
*/
const LessEqualCompare = 515;
/**
* Pass if the incoming value is greater than the texture value.
*
* @type {number}
* @constant
*/
const GreaterCompare = 516;
/**
* Pass if the incoming value is not equal to the texture value.
*
* @type {number}
* @constant
*/
const NotEqualCompare = 517;
/**
* Pass if the incoming value is greater than or equal to the texture value.
*
* @type {number}
* @constant
*/
const GreaterEqualCompare = 518;
/**
* Always pass.
*
* @type {number}
* @constant
*/
const AlwaysCompare = 519;
/**
* The contents are intended to be specified once by the application, and used many
* times as the source for drawing and image specification commands.
*
* @type {number}
* @constant
*/
const StaticDrawUsage = 35044;
/**
* The contents are intended to be respecified repeatedly by the application, and
* used many times as the source for drawing and image specification commands.
*
* @type {number}
* @constant
*/
const DynamicDrawUsage = 35048;
/**
* GLSL 3 shader code.
*
* @type {string}
* @constant
*/
const GLSL3 = '300 es';
/**
* WebGL coordinate system.
*
* @type {number}
* @constant
*/
const WebGLCoordinateSystem = 2000;
/**
* WebGPU coordinate system.
*
* @type {number}
* @constant
*/
const WebGPUCoordinateSystem = 2001;
/**
* This type represents mouse buttons and interaction types in context of controls.
*
* @typedef {Object} ConstantsMouse
* @property {number} MIDDLE - The left mouse button.
* @property {number} LEFT - The middle mouse button.
* @property {number} RIGHT - The right mouse button.
* @property {number} ROTATE - A rotate interaction.
* @property {number} DOLLY - A dolly interaction.
* @property {number} PAN - A pan interaction.
**/
/**
* This type represents touch interaction types in context of controls.
*
* @typedef {Object} ConstantsTouch
* @property {number} ROTATE - A rotate interaction.
* @property {number} PAN - A pan interaction.
* @property {number} DOLLY_PAN - The dolly-pan interaction.
* @property {number} DOLLY_ROTATE - A dolly-rotate interaction.
**/
/**
* This type represents the different timestamp query types.
*
* @typedef {Object} ConstantsTimestampQuery
* @property {string} COMPUTE - A `compute` timestamp query.
* @property {string} RENDER - A `render` timestamp query.
**/
/**
* Represents the different interpolation sampling types.
*
* @typedef {Object} ConstantsInterpolationSamplingType
* @property {string} PERSPECTIVE - Perspective-correct interpolation.
* @property {string} LINEAR - Linear interpolation.
* @property {string} FLAT - Flat interpolation.
*/
/**
* Represents the different interpolation sampling modes.
*
* @typedef {Object} ConstantsInterpolationSamplingMode
* @property {string} NORMAL - Normal sampling mode.
* @property {string} CENTROID - Centroid sampling mode.
* @property {string} SAMPLE - Sample-specific sampling mode.
* @property {string} FLAT_FIRST - Flat interpolation using the first vertex.
* @property {string} FLAT_EITHER - Flat interpolation using either vertex.
*/
/**
* This modules allows to dispatch event objects on custom JavaScript objects.
*
* Main repository: [eventdispatcher.js]{@link https://github.com/mrdoob/eventdispatcher.js/}
*
* Code Example:
* ```js
* class Car extends EventDispatcher {
* start() {
* this.dispatchEvent( { type: 'start', message: 'vroom vroom!' } );
* }
*};
*
* // Using events with the custom object
* const car = new Car();
* car.addEventListener( 'start', function ( event ) {
* alert( event.message );
* } );
*
* car.start();
* ```
*/
class EventDispatcher {
/**
* Adds the given event listener to the given event type.
*
* @param {string} type - The type of event to listen to.
* @param {Function} listener - The function that gets called when the event is fired.
*/
addEventListener( type, listener ) {
if ( this._listeners === undefined ) this._listeners = {};
const listeners = this._listeners;
if ( listeners[ type ] === undefined ) {
listeners[ type ] = [];
}
if ( listeners[ type ].indexOf( listener ) === -1 ) {
listeners[ type ].push( listener );
}
}
/**
* Returns `true` if the given event listener has been added to the given event type.
*
* @param {string} type - The type of event.
* @param {Function} listener - The listener to check.
* @return {boolean} Whether the given event listener has been added to the given event type.
*/
hasEventListener( type, listener ) {
const listeners = this._listeners;
if ( listeners === undefined ) return false;
return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== -1;
}
/**
* Removes the given event listener from the given event type.
*
* @param {string} type - The type of event.
* @param {Function} listener - The listener to remove.
*/
removeEventListener( type, listener ) {
const listeners = this._listeners;
if ( listeners === undefined ) return;
const listenerArray = listeners[ type ];
if ( listenerArray !== undefined ) {
const index = listenerArray.indexOf( listener );
if ( index !== -1 ) {
listenerArray.splice( index, 1 );
}
}
}
/**
* Dispatches an event object.
*
* @param {Object} event - The event that gets fired.
*/
dispatchEvent( event ) {
const listeners = this._listeners;
if ( listeners === undefined ) return;
const listenerArray = listeners[ event.type ];
if ( listenerArray !== undefined ) {
event.target = this;
// Make a copy, in case listeners are removed while iterating.
const array = listenerArray.slice( 0 );
for ( let i = 0, l = array.length; i < l; i ++ ) {
array[ i ].call( this, event );
}
event.target = null;
}
}
}
const _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ];
const DEG2RAD = Math.PI / 180;
const RAD2DEG = 180 / Math.PI;
/**
* Generate a [UUID]{@link https://en.wikipedia.org/wiki/Universally_unique_identifier}
* (universally unique identifier).
*
* @return {string} The UUID.
*/
function generateUUID() {
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
const d0 = Math.random() * 0xffffffff | 0;
const d1 = Math.random() * 0xffffffff | 0;
const d2 = Math.random() * 0xffffffff | 0;
const d3 = Math.random() * 0xffffffff | 0;
const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' +
_lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' +
_lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] +
_lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ];
// .toLowerCase() here flattens concatenated strings to save heap memory space.
return uuid.toLowerCase();
}
/**
* Clamps the given value between min and max.
*
* @param {number} value - The value to clamp.
* @param {number} min - The min value.
* @param {number} max - The max value.
* @return {number} The clamped value.
*/
function clamp( value, min, max ) {
return Math.max( min, Math.min( max, value ) );
}
/**
* Computes the Euclidean modulo of the given parameters that
* is `( ( n % m ) + m ) % m`.
*
* @param {number} n - The first parameter.
* @param {number} m - The second parameter.
* @return {number} The Euclidean modulo.
*/
function euclideanModulo( n, m ) {
// https://en.wikipedia.org/wiki/Modulo_operation
return ( ( n % m ) + m ) % m;
}
/**
* Returns a value linearly interpolated from two known points based on the given interval -
* `t = 0` will return `x` and `t = 1` will return `y`.
*
* @param {number} x - The start point
* @param {number} y - The end point.
* @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
* @return {number} The interpolated value.
*/
function lerp( x, y, t ) {
return ( 1 - t ) * x + t * y;
}
/**
* Denormalizes the given value according to the given typed array.
*
* @param {number} value - The value to denormalize.
* @param {TypedArray} array - The typed array that defines the data type of the value.
* @return {number} The denormalize (float) value in the range `[0,1]`.
*/
function denormalize( value, array ) {
switch ( array.constructor ) {
case Float32Array:
return value;
case Uint32Array:
return value / 4294967295.0;
case Uint16Array:
return value / 65535.0;
case Uint8Array:
return value / 255.0;
case Int32Array:
return Math.max( value / 2147483647.0, -1 );
case Int16Array:
return Math.max( value / 32767.0, -1 );
case Int8Array:
return Math.max( value / 127.0, -1 );
default:
throw new Error( 'Invalid component type.' );
}
}
/**
* Normalizes the given value according to the given typed array.
*
* @param {number} value - The float value in the range `[0,1]` to normalize.
* @param {TypedArray} array - The typed array that defines the data type of the value.
* @return {number} The normalize value.
*/
function normalize( value, array ) {
switch ( array.constructor ) {
case Float32Array:
return value;
case Uint32Array:
return Math.round( value * 4294967295.0 );
case Uint16Array:
return Math.round( value * 65535.0 );
case Uint8Array:
return Math.round( value * 255.0 );
case Int32Array:
return Math.round( value * 2147483647.0 );
case Int16Array:
return Math.round( value * 32767.0 );
case Int8Array:
return Math.round( value * 127.0 );
default:
throw new Error( 'Invalid component type.' );
}
}
/**
* Class representing a 2D vector. A 2D vector is an ordered pair of numbers
* (labeled x and y), which can be used to represent a number of things, such as:
*
* - A point in 2D space (i.e. a position on a plane).
* - A direction and length across a plane. In three.js the length will
* always be the Euclidean distance(straight-line distance) from `(0, 0)` to `(x, y)`
* and the direction is also measured from `(0, 0)` towards `(x, y)`.
* - Any arbitrary ordered pair of numbers.
*
* There are other things a 2D vector can be used to represent, such as
* momentum vectors, complex numbers and so on, however these are the most
* common uses in three.js.
*
* Iterating through a vector instance will yield its components `(x, y)` in
* the corresponding order.
* ```js
* const a = new THREE.Vector2( 0, 1 );
*
* //no arguments; will be initialised to (0, 0)
* const b = new THREE.Vector2( );
*
* const d = a.distanceTo( b );
* ```
*/
class Vector2$1 {
/**
* Constructs a new 2D vector.
*
* @param {number} [x=0] - The x value of this vector.
* @param {number} [y=0] - The y value of this vector.
*/
constructor( x = 0, y = 0 ) {
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
Vector2$1.prototype.isVector2 = true;
/**
* The x value of this vector.
*
* @type {number}
*/
this.x = x;
/**
* The y value of this vector.
*
* @type {number}
*/
this.y = y;
}
/**
* Alias for {@link Vector2#x}.
*
* @type {number}
*/
get width() {
return this.x;
}
set width( value ) {
this.x = value;
}
/**
* Alias for {@link Vector2#y}.
*
* @type {number}
*/
get height() {
return this.y;
}
set height( value ) {
this.y = value;
}
/**
* Sets the vector components.
*
* @param {number} x - The value of the x component.
* @param {number} y - The value of the y component.
* @return {Vector2} A reference to this vector.
*/
set( x, y ) {
this.x = x;
this.y = y;
return this;
}
/**
* Sets the vector components to the same value.
*
* @param {number} scalar - The value to set for all vector components.
* @return {Vector2} A reference to this vector.
*/
setScalar( scalar ) {
this.x = scalar;
this.y = scalar;
return this;
}
/**
* Sets the vector's x component to the given value
*
* @param {number} x - The value to set.
* @return {Vector2} A reference to this vector.
*/
setX( x ) {
this.x = x;
return this;
}
/**
* Sets the vector's y component to the given value
*
* @param {number} y - The value to set.
* @return {Vector2} A reference to this vector.
*/
setY( y ) {
this.y = y;
return this;
}
/**
* Allows to set a vector component with an index.
*
* @param {number} index - The component index. `0` equals to x, `1` equals to y.
* @param {number} value - The value to set.
* @return {Vector2} A reference to this vector.
*/
setComponent( index, value ) {
switch ( index ) {
case 0: this.x = value; break;
case 1: this.y = value; break;
default: throw new Error( 'index is out of range: ' + index );
}
return this;
}
/**
* Returns the value of the vector component which matches the given index.
*
* @param {number} index - The component index. `0` equals to x, `1` equals to y.
* @return {number} A vector component value.
*/
getComponent( index ) {
switch ( index ) {
case 0: return this.x;
case 1: return this.y;
default: throw new Error( 'index is out of range: ' + index );
}
}
/**
* Returns a new vector with copied values from this instance.
*
* @return {Vector2} A clone of this instance.
*/
clone() {
return new this.constructor( this.x, this.y );
}
/**
* Copies the values of the given vector to this instance.
*
* @param {Vector2} v - The vector to copy.
* @return {Vector2} A reference to this vector.
*/
copy( v ) {
this.x = v.x;
this.y = v.y;
return this;
}
/**
* Adds the given vector to this instance.
*
* @param {Vector2} v - The vector to add.
* @return {Vector2} A reference to this vector.
*/
add( v ) {
this.x += v.x;
this.y += v.y;
return this;
}
/**
* Adds the given scalar value to all components of this instance.
*
* @param {number} s - The scalar to add.
* @return {Vector2} A reference to this vector.
*/
addScalar( s ) {
this.x += s;
this.y += s;
return this;
}
/**
* Adds the given vectors and stores the result in this instance.
*
* @param {Vector2} a - The first vector.
* @param {Vector2} b - The second vector.
* @return {Vector2} A reference to this vector.
*/
addVectors( a, b ) {
this.x = a.x + b.x;
this.y = a.y + b.y;
return this;
}
/**
* Adds the given vector scaled by the given factor to this instance.
*
* @param {Vector2} v - The vector.
* @param {number} s - The factor that scales `v`.
* @return {Vector2} A reference to this vector.
*/
addScaledVector( v, s ) {
this.x += v.x * s;
this.y += v.y * s;
return this;
}
/**
* Subtracts the given vector from this instance.
*
* @param {Vector2} v - The vector to subtract.
* @return {Vector2} A reference to this vector.
*/
sub( v ) {
this.x -= v.x;
this.y -= v.y;
return this;
}
/**
* Subtracts the given scalar value from all components of this instance.
*
* @param {number} s - The scalar to subtract.
* @return {Vector2} A reference to this vector.
*/
subScalar( s ) {
this.x -= s;
this.y -= s;
return this;
}
/**
* Subtracts the given vectors and stores the result in this instance.
*
* @param {Vector2} a - The first vector.
* @param {Vector2} b - The second vector.
* @return {Vector2} A reference to this vector.
*/
subVectors( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
return this;
}
/**
* Multiplies the given vector with this instance.
*
* @param {Vector2} v - The vector to multiply.
* @return {Vector2} A reference to this vector.
*/
multiply( v ) {
this.x *= v.x;
this.y *= v.y;
return this;
}
/**
* Multiplies the given scalar value with all components of this instance.
*
* @param {number} scalar - The scalar to multiply.
* @return {Vector2} A reference to this vector.
*/
multiplyScalar( scalar ) {
this.x *= scalar;
this.y *= scalar;
return this;
}
/**
* Divides this instance by the given vector.
*
* @param {Vector2} v - The vector to divide.
* @return {Vector2} A reference to this vector.
*/
divide( v ) {
this.x /= v.x;
this.y /= v.y;
return this;
}
/**
* Divides this vector by the given scalar.
*
* @param {number} scalar - The scalar to divide.
* @return {Vector2} A reference to this vector.
*/
divideScalar( scalar ) {
return this.multiplyScalar( 1 / scalar );
}
/**
* Multiplies this vector (with an implicit 1 as the 3rd component) by
* the given 3x3 matrix.
*
* @param {Matrix3} m - The matrix to apply.
* @return {Vector2} A reference to this vector.
*/
applyMatrix3( m ) {
const x = this.x, y = this.y;
const e = m.elements;
this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];
this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];
return this;
}
/**
* If this vector's x or y value is greater than the given vector's x or y
* value, replace that value with the corresponding min value.
*
* @param {Vector2} v - The vector.
* @return {Vector2} A reference to this vector.
*/
min( v ) {
this.x = Math.min( this.x, v.x );
this.y = Math.min( this.y, v.y );
return this;
}
/**
* If this vector's x or y value is less than the given vector's x or y
* value, replace that value with the corresponding max value.
*
* @param {Vector2} v - The vector.
* @return {Vector2} A reference to this vector.
*/
max( v ) {
this.x = Math.max( this.x, v.x );
this.y = Math.max( this.y, v.y );
return this;
}
/**
* If this vector's x or y value is greater than the max vector's x or y
* value, it is replaced by the corresponding value.
* If this vector's x or y value is less than the min vector's x or y value,
* it is replaced by the corresponding value.
*
* @param {Vector2} min - The minimum x and y values.
* @param {Vector2} max - The maximum x and y values in the desired range.
* @return {Vector2} A reference to this vector.
*/
clamp( min, max ) {
// assumes min < max, componentwise
this.x = clamp( this.x, min.x, max.x );
this.y = clamp( this.y, min.y, max.y );
return this;
}
/**
* If this vector's x or y values are greater than the max value, they are
* replaced by the max value.
* If this vector's x or y values are less than the min value, they are
* replaced by the min value.
*
* @param {number} minVal - The minimum value the components will be clamped to.
* @param {number} maxVal - The maximum value the components will be clamped to.
* @return {Vector2} A reference to this vector.
*/
clampScalar( minVal, maxVal ) {
this.x = clamp( this.x, minVal, maxVal );
this.y = clamp( this.y, minVal, maxVal );
return this;
}
/**
* If this vector's length is greater than the max value, it is replaced by
* the max value.
* If this vector's length is less than the min value, it is replaced by the
* min value.
*
* @param {number} min - The minimum value the vector length will be clamped to.
* @param {number} max - The maximum value the vector length will be clamped to.
* @return {Vector2} A reference to this vector.
*/
clampLength( min, max ) {
const length = this.length();
return this.divideScalar( length || 1 ).multiplyScalar( clamp( length, min, max ) );
}
/**
* The components of this vector are rounded down to the nearest integer value.
*
* @return {Vector2} A reference to this vector.
*/
floor() {
this.x = Math.floor( this.x );
this.y = Math.floor( this.y );
return this;
}
/**
* The components of this vector are rounded up to the nearest integer value.
*
* @return {Vector2} A reference to this vector.
*/
ceil() {
this.x = Math.ceil( this.x );
this.y = Math.ceil( this.y );
return this;
}
/**
* The components of this vector are rounded to the nearest integer value
*
* @return {Vector2} A reference to this vector.
*/
round() {
this.x = Math.round( this.x );
this.y = Math.round( this.y );
return this;
}
/**
* The components of this vector are rounded towards zero (up if negative,
* down if positive) to an integer value.
*
* @return {Vector2} A reference to this vector.
*/
roundToZero() {
this.x = Math.trunc( this.x );
this.y = Math.trunc( this.y );
return this;
}
/**
* Inverts this vector - i.e. sets x = -x and y = -y.
*
* @return {Vector2} A reference to this vector.
*/
negate() {
this.x = - this.x;
this.y = - this.y;
return this;
}
/**
* Calculates the dot product of the given vector with this instance.
*
* @param {Vector2} v - The vector to compute the dot product with.
* @return {number} The result of the dot product.
*/
dot( v ) {
return this.x * v.x + this.y * v.y;
}
/**
* Calculates the cross product of the given vector with this instance.
*
* @param {Vector2} v - The vector to compute the cross product with.
* @return {number} The result of the cross product.
*/
cross( v ) {
return this.x * v.y - this.y * v.x;
}
/**
* Computes the square of the Euclidean length (straight-line length) from
* (0, 0) to (x, y). If you are comparing the lengths of vectors, you should
* compare the length squared instead as it is slightly more efficient to calculate.
*
* @return {number} The square length of this vector.
*/
lengthSq() {
return this.x * this.x + this.y * this.y;
}
/**
* Computes the Euclidean length (straight-line length) from (0, 0) to (x, y).
*
* @return {number} The length of this vector.
*/
length() {
return Math.sqrt( this.x * this.x + this.y * this.y );
}
/**
* Computes the Manhattan length of this vector.
*
* @return {number} The length of this vector.
*/
manhattanLength() {
return Math.abs( this.x ) + Math.abs( this.y );
}
/**
* Converts this vector to a unit vector - that is, sets it equal to a vector
* with the same direction as this one, but with a vector length of `1`.
*
* @return {Vector2} A reference to this vector.
*/
normalize() {
return this.divideScalar( this.length() || 1 );
}
/**
* Computes the angle in radians of this vector with respect to the positive x-axis.
*
* @return {number} The angle in radians.
*/
angle() {
const angle = Math.atan2( - this.y, - this.x ) + Math.PI;
return angle;
}
/**
* Returns the angle between the given vector and this instance in radians.
*
* @param {Vector2} v - The vector to compute the angle with.
* @return {number} The angle in radians.
*/
angleTo( v ) {
const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
if ( denominator === 0 ) return Math.PI / 2;
const theta = this.dot( v ) / denominator;
// clamp, to handle numerical problems
return Math.acos( clamp( theta, -1, 1 ) );
}
/**
* Computes the distance from the given vector to this instance.
*
* @param {Vector2} v - The vector to compute the distance to.
* @return {number} The distance.
*/
distanceTo( v ) {
return Math.sqrt( this.distanceToSquared( v ) );
}
/**
* Computes the squared distance from the given vector to this instance.
* If you are just comparing the distance with another distance, you should compare
* the distance squared instead as it is slightly more efficient to calculate.
*
* @param {Vector2} v - The vector to compute the squared distance to.
* @return {number} The squared distance.
*/
distanceToSquared( v ) {
const dx = this.x - v.x, dy = this.y - v.y;
return dx * dx + dy * dy;
}
/**
* Computes the Manhattan distance from the given vector to this instance.
*
* @param {Vector2} v - The vector to compute the Manhattan distance to.
* @return {number} The Manhattan distance.
*/
manhattanDistanceTo( v ) {
return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
}
/**
* Sets this vector to a vector with the same direction as this one, but
* with the specified length.
*
* @param {number} length - The new length of this vector.
* @return {Vector2} A reference to this vector.
*/
setLength( length ) {
return this.normalize().multiplyScalar( length );
}
/**
* Linearly interpolates between the given vector and this instance, where
* alpha is the percent distance along the line - alpha = 0 will be this
* vector, and alpha = 1 will be the given one.
*
* @param {Vector2} v - The vector to interpolate towards.
* @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
* @return {Vector2} A reference to this vector.
*/
lerp( v, alpha ) {
this.x += ( v.x - this.x ) * alpha;
this.y += ( v.y - this.y ) * alpha;
return this;
}
/**
* Linearly interpolates between the given vectors, where alpha is the percent
* distance along the line - alpha = 0 will be first vector, and alpha = 1 will
* be the second one. The result is stored in this instance.
*
* @param {Vector2} v1 - The first vector.
* @param {Vector2} v2 - The second vector.
* @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
* @return {Vector2} A reference to this vector.
*/
lerpVectors( v1, v2, alpha ) {
this.x = v1.x + ( v2.x - v1.x ) * alpha;
this.y = v1.y + ( v2.y - v1.y ) * alpha;
return this;
}
/**
* Returns `true` if this vector is equal with the given one.
*
* @param {Vector2} v - The vector to test for equality.
* @return {boolean} Whether this vector is equal with the given one.
*/
equals( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) );
}
/**
* Sets this vector's x value to be `array[ offset ]` and y
* value to be `array[ offset + 1 ]`.
*
* @param {Array} array - An array holding the vector component values.
* @param {number} [offset=0] - The offset into the array.
* @return {Vector2} A reference to this vector.
*/
fromArray( array, offset = 0 ) {
this.x = array[ offset ];
this.y = array[ offset + 1 ];
return this;
}
/**
* Writes the components of this vector to the given array. If no array is provided,
* the method returns a new instance.
*
* @param {Array} [array=[]] - The target array holding the vector components.
* @param {number} [offset=0] - Index of the first element in the array.
* @return {Array} The vector components.
*/
toArray( array = [], offset = 0 ) {
array[ offset ] = this.x;
array[ offset + 1 ] = this.y;
return array;
}
/**
* Sets the components of this vector from the given buffer attribute.
*
* @param {BufferAttribute} attribute - The buffer attribute holding vector data.
* @param {number} index - The index into the attribute.
* @return {Vector2} A reference to this vector.
*/
fromBufferAttribute( attribute, index ) {
this.x = attribute.getX( index );
this.y = attribute.getY( index );
return this;
}
/**
* Rotates this vector around the given center by the given angle.
*
* @param {Vector2} center - The point around which to rotate.
* @param {number} angle - The angle to rotate, in radians.
* @return {Vector2} A reference to this vector.
*/
rotateAround( center, angle ) {
const c = Math.cos( angle ), s = Math.sin( angle );
const x = this.x - center.x;
const y = this.y - center.y;
this.x = x * c - y * s + center.x;
this.y = x * s + y * c + center.y;
return this;
}
/**
* Sets each component of this vector to a pseudo-random value between `0` and
* `1`, excluding `1`.
*
* @return {Vector2} A reference to this vector.
*/
random() {
this.x = Math.random();
this.y = Math.random();
return this;
}
*[ Symbol.iterator ]() {
yield this.x;
yield this.y;
}
}
/**
* Class for representing a Quaternion. Quaternions are used in three.js to represent rotations.
*
* Iterating through a vector instance will yield its components `(x, y, z, w)` in
* the corresponding order.
*
* Note that three.js expects Quaternions to be normalized.
* ```js
* const quaternion = new THREE.Quaternion();
* quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 2 );
*
* const vector = new THREE.Vector3( 1, 0, 0 );
* vector.applyQuaternion( quaternion );
* ```
*/
class Quaternion {
/**
* Constructs a new quaternion.
*
* @param {number} [x=0] - The x value of this quaternion.
* @param {number} [y=0] - The y value of this quaternion.
* @param {number} [z=0] - The z value of this quaternion.
* @param {number} [w=1] - The w value of this quaternion.
*/
constructor( x = 0, y = 0, z = 0, w = 1 ) {
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isQuaternion = true;
this._x = x;
this._y = y;
this._z = z;
this._w = w;
}
/**
* Interpolates between two quaternions via SLERP. This implementation assumes the
* quaternion data are managed in flat arrays.
*
* @param {Array} dst - The destination array.
* @param {number} dstOffset - An offset into the destination array.
* @param {Array} src0 - The source array of the first quaternion.
* @param {number} srcOffset0 - An offset into the first source array.
* @param {Array} src1 - The source array of the second quaternion.
* @param {number} srcOffset1 - An offset into the second source array.
* @param {number} t - The interpolation factor in the range `[0,1]`.
* @see {@link Quaternion#slerp}
*/
static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
// fuzz-free, array-based Quaternion SLERP operation
let x0 = src0[ srcOffset0 + 0 ],
y0 = src0[ srcOffset0 + 1 ],
z0 = src0[ srcOffset0 + 2 ],
w0 = src0[ srcOffset0 + 3 ];
const x1 = src1[ srcOffset1 + 0 ],
y1 = src1[ srcOffset1 + 1 ],
z1 = src1[ srcOffset1 + 2 ],
w1 = src1[ srcOffset1 + 3 ];
if ( t === 0 ) {
dst[ dstOffset + 0 ] = x0;
dst[ dstOffset + 1 ] = y0;
dst[ dstOffset + 2 ] = z0;
dst[ dstOffset + 3 ] = w0;
return;
}
if ( t === 1 ) {
dst[ dstOffset + 0 ] = x1;
dst[ dstOffset + 1 ] = y1;
dst[ dstOffset + 2 ] = z1;
dst[ dstOffset + 3 ] = w1;
return;
}
if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
let s = 1 - t;
const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
dir = ( cos >= 0 ? 1 : -1 ),
sqrSin = 1 - cos * cos;
// Skip the Slerp for tiny steps to avoid numeric problems:
if ( sqrSin > Number.EPSILON ) {
const sin = Math.sqrt( sqrSin ),
len = Math.atan2( sin, cos * dir );
s = Math.sin( s * len ) / sin;
t = Math.sin( t * len ) / sin;
}
const tDir = t * dir;
x0 = x0 * s + x1 * tDir;
y0 = y0 * s + y1 * tDir;
z0 = z0 * s + z1 * tDir;
w0 = w0 * s + w1 * tDir;
// Normalize in case we just did a lerp:
if ( s === 1 - t ) {
const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
x0 *= f;
y0 *= f;
z0 *= f;
w0 *= f;
}
}
dst[ dstOffset ] = x0;
dst[ dstOffset + 1 ] = y0;
dst[ dstOffset + 2 ] = z0;
dst[ dstOffset + 3 ] = w0;
}
/**
* Multiplies two quaternions. This implementation assumes the quaternion data are managed
* in flat arrays.
*
* @param {Array} dst - The destination array.
* @param {number} dstOffset - An offset into the destination array.
* @param {Array} src0 - The source array of the first quaternion.
* @param {number} srcOffset0 - An offset into the first source array.
* @param {Array} src1 - The source array of the second quaternion.
* @param {number} srcOffset1 - An offset into the second source array.
* @return {Array} The destination array.
* @see {@link Quaternion#multiplyQuaternions}.
*/
static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {
const x0 = src0[ srcOffset0 ];
const y0 = src0[ srcOffset0 + 1 ];
const z0 = src0[ srcOffset0 + 2 ];
const w0 = src0[ srcOffset0 + 3 ];
const x1 = src1[ srcOffset1 ];
const y1 = src1[ srcOffset1 + 1 ];
const z1 = src1[ srcOffset1 + 2 ];
const w1 = src1[ srcOffset1 + 3 ];
dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;
dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;
dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;
dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
return dst;
}
/**
* The x value of this quaternion.
*
* @type {number}
* @default 0
*/
get x() {
return this._x;
}
set x( value ) {
this._x = value;
this._onChangeCallback();
}
/**
* The y value of this quaternion.
*
* @type {number}
* @default 0
*/
get y() {
return this._y;
}
set y( value ) {
this._y = value;
this._onChangeCallback();
}
/**
* The z value of this quaternion.
*
* @type {number}
* @default 0
*/
get z() {
return this._z;
}
set z( value ) {
this._z = value;
this._onChangeCallback();
}
/**
* The w value of this quaternion.
*
* @type {number}
* @default 1
*/
get w() {
return this._w;
}
set w( value ) {
this._w = value;
this._onChangeCallback();
}
/**
* Sets the quaternion components.
*
* @param {number} x - The x value of this quaternion.
* @param {number} y - The y value of this quaternion.
* @param {number} z - The z value of this quaternion.
* @param {number} w - The w value of this quaternion.
* @return {Quaternion} A reference to this quaternion.
*/
set( x, y, z, w ) {
this._x = x;
this._y = y;
this._z = z;
this._w = w;
this._onChangeCallback();
return this;
}
/**
* Returns a new quaternion with copied values from this instance.
*
* @return {Quaternion} A clone of this instance.
*/
clone() {
return new this.constructor( this._x, this._y, this._z, this._w );
}
/**
* Copies the values of the given quaternion to this instance.
*
* @param {Quaternion} quaternion - The quaternion to copy.
* @return {Quaternion} A reference to this quaternion.
*/
copy( quaternion ) {
this._x = quaternion.x;
this._y = quaternion.y;
this._z = quaternion.z;
this._w = quaternion.w;
this._onChangeCallback();
return this;
}
/**
* Sets this quaternion from the rotation specified by the given
* Euler angles.
*
* @param {Euler} euler - The Euler angles.
* @param {boolean} [update=true] - Whether the internal `onChange` callback should be executed or not.
* @return {Quaternion} A reference to this quaternion.
*/
setFromEuler( euler, update = true ) {
const x = euler._x, y = euler._y, z = euler._z, order = euler._order;
// http://www.mathworks.com/matlabcentral/fileexchange/
// 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
// content/SpinCalc.m
const cos = Math.cos;
const sin = Math.sin;
const c1 = cos( x / 2 );
const c2 = cos( y / 2 );
const c3 = cos( z / 2 );
const s1 = sin( x / 2 );
const s2 = sin( y / 2 );
const s3 = sin( z / 2 );
switch ( order ) {
case 'XYZ':
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
break;
case 'YXZ':
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
break;
case 'ZXY':
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
break;
case 'ZYX':
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
break;
case 'YZX':
this._x = s1 * c2 * c3 + c1 * s2 * s3;
this._y = c1 * s2 * c3 + s1 * c2 * s3;
this._z = c1 * c2 * s3 - s1 * s2 * c3;
this._w = c1 * c2 * c3 - s1 * s2 * s3;
break;
case 'XZY':
this._x = s1 * c2 * c3 - c1 * s2 * s3;
this._y = c1 * s2 * c3 - s1 * c2 * s3;
this._z = c1 * c2 * s3 + s1 * s2 * c3;
this._w = c1 * c2 * c3 + s1 * s2 * s3;
break;
default:
console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );
}
if ( update === true ) this._onChangeCallback();
return this;
}
/**
* Sets this quaternion from the given axis and angle.
*
* @param {Vector3} axis - The normalized axis.
* @param {number} angle - The angle in radians.
* @return {Quaternion} A reference to this quaternion.
*/
setFromAxisAngle( axis, angle ) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
const halfAngle = angle / 2, s = Math.sin( halfAngle );
this._x = axis.x * s;
this._y = axis.y * s;
this._z = axis.z * s;
this._w = Math.cos( halfAngle );
this._onChangeCallback();
return this;
}
/**
* Sets this quaternion from the given rotation matrix.
*
* @param {Matrix4} m - A 4x4 matrix of which the upper 3x3 of matrix is a pure rotation matrix (i.e. unscaled).
* @return {Quaternion} A reference to this quaternion.
*/
setFromRotationMatrix( m ) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
const te = m.elements,
m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
trace = m11 + m22 + m33;
if ( trace > 0 ) {
const s = 0.5 / Math.sqrt( trace + 1.0 );
this._w = 0.25 / s;
this._x = ( m32 - m23 ) * s;
this._y = ( m13 - m31 ) * s;
this._z = ( m21 - m12 ) * s;
} else if ( m11 > m22 && m11 > m33 ) {
const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
this._w = ( m32 - m23 ) / s;
this._x = 0.25 * s;
this._y = ( m12 + m21 ) / s;
this._z = ( m13 + m31 ) / s;
} else if ( m22 > m33 ) {
const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
this._w = ( m13 - m31 ) / s;
this._x = ( m12 + m21 ) / s;
this._y = 0.25 * s;
this._z = ( m23 + m32 ) / s;
} else {
const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
this._w = ( m21 - m12 ) / s;
this._x = ( m13 + m31 ) / s;
this._y = ( m23 + m32 ) / s;
this._z = 0.25 * s;
}
this._onChangeCallback();
return this;
}
/**
* Sets this quaternion to the rotation required to rotate the direction vector
* `vFrom` to the direction vector `vTo`.
*
* @param {Vector3} vFrom - The first (normalized) direction vector.
* @param {Vector3} vTo - The second (normalized) direction vector.
* @return {Quaternion} A reference to this quaternion.
*/
setFromUnitVectors( vFrom, vTo ) {
// assumes direction vectors vFrom and vTo are normalized
let r = vFrom.dot( vTo ) + 1;
if ( r < Number.EPSILON ) {
// vFrom and vTo point in opposite directions
r = 0;
if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
this._x = - vFrom.y;
this._y = vFrom.x;
this._z = 0;
this._w = r;
} else {
this._x = 0;
this._y = - vFrom.z;
this._z = vFrom.y;
this._w = r;
}
} else {
// crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3
this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
this._w = r;
}
return this.normalize();
}
/**
* Returns the angle between this quaternion and the given one in radians.
*
* @param {Quaternion} q - The quaternion to compute the angle with.
* @return {number} The angle in radians.
*/
angleTo( q ) {
return 2 * Math.acos( Math.abs( clamp( this.dot( q ), -1, 1 ) ) );
}
/**
* Rotates this quaternion by a given angular step to the given quaternion.
* The method ensures that the final quaternion will not overshoot `q`.
*
* @param {Quaternion} q - The target quaternion.
* @param {number} step - The angular step in radians.
* @return {Quaternion} A reference to this quaternion.
*/
rotateTowards( q, step ) {
const angle = this.angleTo( q );
if ( angle === 0 ) return this;
const t = Math.min( 1, step / angle );
this.slerp( q, t );
return this;
}
/**
* Sets this quaternion to the identity quaternion; that is, to the
* quaternion that represents "no rotation".
*
* @return {Quaternion} A reference to this quaternion.
*/
identity() {
return this.set( 0, 0, 0, 1 );
}
/**
* Inverts this quaternion via {@link Quaternion#conjugate}. The
* quaternion is assumed to have unit length.
*
* @return {Quaternion} A reference to this quaternion.
*/
invert() {
return this.conjugate();
}
/**
* Returns the rotational conjugate of this quaternion. The conjugate of a
* quaternion represents the same rotation in the opposite direction about
* the rotational axis.
*
* @return {Quaternion} A reference to this quaternion.
*/
conjugate() {
this._x *= -1;
this._y *= -1;
this._z *= -1;
this._onChangeCallback();
return this;
}
/**
* Calculates the dot product of this quaternion and the given one.
*
* @param {Quaternion} v - The quaternion to compute the dot product with.
* @return {number} The result of the dot product.
*/
dot( v ) {
return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
}
/**
* Computes the squared Euclidean length (straight-line length) of this quaternion,
* considered as a 4 dimensional vector. This can be useful if you are comparing the
* lengths of two quaternions, as this is a slightly more efficient calculation than
* {@link Quaternion#length}.
*
* @return {number} The squared Euclidean length.
*/
lengthSq() {
return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
}
/**
* Computes the Euclidean length (straight-line length) of this quaternion,
* considered as a 4 dimensional vector.
*
* @return {number} The Euclidean length.
*/
length() {
return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
}
/**
* Normalizes this quaternion - that is, calculated the quaternion that performs
* the same rotation as this one, but has a length equal to `1`.
*
* @return {Quaternion} A reference to this quaternion.
*/
normalize() {
let l = this.length();
if ( l === 0 ) {
this._x = 0;
this._y = 0;
this._z = 0;
this._w = 1;
} else {
l = 1 / l;
this._x = this._x * l;
this._y = this._y * l;
this._z = this._z * l;
this._w = this._w * l;
}
this._onChangeCallback();
return this;
}
/**
* Multiplies this quaternion by the given one.
*
* @param {Quaternion} q - The quaternion.
* @return {Quaternion} A reference to this quaternion.
*/
multiply( q ) {
return this.multiplyQuaternions( this, q );
}
/**
* Pre-multiplies this quaternion by the given one.
*
* @param {Quaternion} q - The quaternion.
* @return {Quaternion} A reference to this quaternion.
*/
premultiply( q ) {
return this.multiplyQuaternions( q, this );
}
/**
* Multiplies the given quaternions and stores the result in this instance.
*
* @param {Quaternion} a - The first quaternion.
* @param {Quaternion} b - The second quaternion.
* @return {Quaternion} A reference to this quaternion.
*/
multiplyQuaternions( a, b ) {
// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
this._onChangeCallback();
return this;
}
/**
* Performs a spherical linear interpolation between quaternions.
*
* @param {Quaternion} qb - The target quaternion.
* @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
* @return {Quaternion} A reference to this quaternion.
*/
slerp( qb, t ) {
if ( t === 0 ) return this;
if ( t === 1 ) return this.copy( qb );
const x = this._x, y = this._y, z = this._z, w = this._w;
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
if ( cosHalfTheta < 0 ) {
this._w = - qb._w;
this._x = - qb._x;
this._y = - qb._y;
this._z = - qb._z;
cosHalfTheta = - cosHalfTheta;
} else {
this.copy( qb );
}
if ( cosHalfTheta >= 1.0 ) {
this._w = w;
this._x = x;
this._y = y;
this._z = z;
return this;
}
const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;
if ( sqrSinHalfTheta <= Number.EPSILON ) {
const s = 1 - t;
this._w = s * w + t * this._w;
this._x = s * x + t * this._x;
this._y = s * y + t * this._y;
this._z = s * z + t * this._z;
this.normalize(); // normalize calls _onChangeCallback()
return this;
}
const sinHalfTheta = Math.sqrt( sqrSinHalfTheta );
const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
this._w = ( w * ratioA + this._w * ratioB );
this._x = ( x * ratioA + this._x * ratioB );
this._y = ( y * ratioA + this._y * ratioB );
this._z = ( z * ratioA + this._z * ratioB );
this._onChangeCallback();
return this;
}
/**
* Performs a spherical linear interpolation between the given quaternions
* and stores the result in this quaternion.
*
* @param {Quaternion} qa - The source quaternion.
* @param {Quaternion} qb - The target quaternion.
* @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
* @return {Quaternion} A reference to this quaternion.
*/
slerpQuaternions( qa, qb, t ) {
return this.copy( qa ).slerp( qb, t );
}
/**
* Sets this quaternion to a uniformly random, normalized quaternion.
*
* @return {Quaternion} A reference to this quaternion.
*/
random() {
// Ken Shoemake
// Uniform random rotations
// D. Kirk, editor, Graphics Gems III, pages 124-132. Academic Press, New York, 1992.
const theta1 = 2 * Math.PI * Math.random();
const theta2 = 2 * Math.PI * Math.random();
const x0 = Math.random();
const r1 = Math.sqrt( 1 - x0 );
const r2 = Math.sqrt( x0 );
return this.set(
r1 * Math.sin( theta1 ),
r1 * Math.cos( theta1 ),
r2 * Math.sin( theta2 ),
r2 * Math.cos( theta2 ),
);
}
/**
* Returns `true` if this quaternion is equal with the given one.
*
* @param {Quaternion} quaternion - The quaternion to test for equality.
* @return {boolean} Whether this quaternion is equal with the given one.
*/
equals( quaternion ) {
return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
}
/**
* Sets this quaternion's components from the given array.
*
* @param {Array} array - An array holding the quaternion component values.
* @param {number} [offset=0] - The offset into the array.
* @return {Quaternion} A reference to this quaternion.
*/
fromArray( array, offset = 0 ) {
this._x = array[ offset ];
this._y = array[ offset + 1 ];
this._z = array[ offset + 2 ];
this._w = array[ offset + 3 ];
this._onChangeCallback();
return this;
}
/**
* Writes the components of this quaternion to the given array. If no array is provided,
* the method returns a new instance.
*
* @param {Array} [array=[]] - The target array holding the quaternion components.
* @param {number} [offset=0] - Index of the first element in the array.
* @return {Array} The quaternion components.
*/
toArray( array = [], offset = 0 ) {
array[ offset ] = this._x;
array[ offset + 1 ] = this._y;
array[ offset + 2 ] = this._z;
array[ offset + 3 ] = this._w;
return array;
}
/**
* Sets the components of this quaternion from the given buffer attribute.
*
* @param {BufferAttribute} attribute - The buffer attribute holding quaternion data.
* @param {number} index - The index into the attribute.
* @return {Quaternion} A reference to this quaternion.
*/
fromBufferAttribute( attribute, index ) {
this._x = attribute.getX( index );
this._y = attribute.getY( index );
this._z = attribute.getZ( index );
this._w = attribute.getW( index );
this._onChangeCallback();
return this;
}
/**
* This methods defines the serialization result of this class. Returns the
* numerical elements of this quaternion in an array of format `[x, y, z, w]`.
*
* @return {Array} The serialized quaternion.
*/
toJSON() {
return this.toArray();
}
_onChange( callback ) {
this._onChangeCallback = callback;
return this;
}
_onChangeCallback() {}
*[ Symbol.iterator ]() {
yield this._x;
yield this._y;
yield this._z;
yield this._w;
}
}
/**
* Class representing a 3D vector. A 3D vector is an ordered triplet of numbers
* (labeled x, y and z), which can be used to represent a number of things, such as:
*
* - A point in 3D space.
* - A direction and length in 3D space. In three.js the length will
* always be the Euclidean distance(straight-line distance) from `(0, 0, 0)` to `(x, y, z)`
* and the direction is also measured from `(0, 0, 0)` towards `(x, y, z)`.
* - Any arbitrary ordered triplet of numbers.
*
* There are other things a 3D vector can be used to represent, such as
* momentum vectors and so on, however these are the most
* common uses in three.js.
*
* Iterating through a vector instance will yield its components `(x, y, z)` in
* the corresponding order.
* ```js
* const a = new THREE.Vector3( 0, 1, 0 );
*
* //no arguments; will be initialised to (0, 0, 0)
* const b = new THREE.Vector3( );
*
* const d = a.distanceTo( b );
* ```
*/
class Vector3$1 {
/**
* Constructs a new 3D vector.
*
* @param {number} [x=0] - The x value of this vector.
* @param {number} [y=0] - The y value of this vector.
* @param {number} [z=0] - The z value of this vector.
*/
constructor( x = 0, y = 0, z = 0 ) {
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
Vector3$1.prototype.isVector3 = true;
/**
* The x value of this vector.
*
* @type {number}
*/
this.x = x;
/**
* The y value of this vector.
*
* @type {number}
*/
this.y = y;
/**
* The z value of this vector.
*
* @type {number}
*/
this.z = z;
}
/**
* Sets the vector components.
*
* @param {number} x - The value of the x component.
* @param {number} y - The value of the y component.
* @param {number} z - The value of the z component.
* @return {Vector3} A reference to this vector.
*/
set( x, y, z ) {
if ( z === undefined ) z = this.z; // sprite.scale.set(x,y)
this.x = x;
this.y = y;
this.z = z;
return this;
}
/**
* Sets the vector components to the same value.
*
* @param {number} scalar - The value to set for all vector components.
* @return {Vector3} A reference to this vector.
*/
setScalar( scalar ) {
this.x = scalar;
this.y = scalar;
this.z = scalar;
return this;
}
/**
* Sets the vector's x component to the given value
*
* @param {number} x - The value to set.
* @return {Vector3} A reference to this vector.
*/
setX( x ) {
this.x = x;
return this;
}
/**
* Sets the vector's y component to the given value
*
* @param {number} y - The value to set.
* @return {Vector3} A reference to this vector.
*/
setY( y ) {
this.y = y;
return this;
}
/**
* Sets the vector's z component to the given value
*
* @param {number} z - The value to set.
* @return {Vector3} A reference to this vector.
*/
setZ( z ) {
this.z = z;
return this;
}
/**
* Allows to set a vector component with an index.
*
* @param {number} index - The component index. `0` equals to x, `1` equals to y, `2` equals to z.
* @param {number} value - The value to set.
* @return {Vector3} A reference to this vector.
*/
setComponent( index, value ) {
switch ( index ) {
case 0: this.x = value; break;
case 1: this.y = value; break;
case 2: this.z = value; break;
default: throw new Error( 'index is out of range: ' + index );
}
return this;
}
/**
* Returns the value of the vector component which matches the given index.
*
* @param {number} index - The component index. `0` equals to x, `1` equals to y, `2` equals to z.
* @return {number} A vector component value.
*/
getComponent( index ) {
switch ( index ) {
case 0: return this.x;
case 1: return this.y;
case 2: return this.z;
default: throw new Error( 'index is out of range: ' + index );
}
}
/**
* Returns a new vector with copied values from this instance.
*
* @return {Vector3} A clone of this instance.
*/
clone() {
return new this.constructor( this.x, this.y, this.z );
}
/**
* Copies the values of the given vector to this instance.
*
* @param {Vector3} v - The vector to copy.
* @return {Vector3} A reference to this vector.
*/
copy( v ) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
/**
* Adds the given vector to this instance.
*
* @param {Vector3} v - The vector to add.
* @return {Vector3} A reference to this vector.
*/
add( v ) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
}
/**
* Adds the given scalar value to all components of this instance.
*
* @param {number} s - The scalar to add.
* @return {Vector3} A reference to this vector.
*/
addScalar( s ) {
this.x += s;
this.y += s;
this.z += s;
return this;
}
/**
* Adds the given vectors and stores the result in this instance.
*
* @param {Vector3} a - The first vector.
* @param {Vector3} b - The second vector.
* @return {Vector3} A reference to this vector.
*/
addVectors( a, b ) {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
return this;
}
/**
* Adds the given vector scaled by the given factor to this instance.
*
* @param {Vector3|Vector4} v - The vector.
* @param {number} s - The factor that scales `v`.
* @return {Vector3} A reference to this vector.
*/
addScaledVector( v, s ) {
this.x += v.x * s;
this.y += v.y * s;
this.z += v.z * s;
return this;
}
/**
* Subtracts the given vector from this instance.
*
* @param {Vector3} v - The vector to subtract.
* @return {Vector3} A reference to this vector.
*/
sub( v ) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
}
/**
* Subtracts the given scalar value from all components of this instance.
*
* @param {number} s - The scalar to subtract.
* @return {Vector3} A reference to this vector.
*/
subScalar( s ) {
this.x -= s;
this.y -= s;
this.z -= s;
return this;
}
/**
* Subtracts the given vectors and stores the result in this instance.
*
* @param {Vector3} a - The first vector.
* @param {Vector3} b - The second vector.
* @return {Vector3} A reference to this vector.
*/
subVectors( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
return this;
}
/**
* Multiplies the given vector with this instance.
*
* @param {Vector3} v - The vector to multiply.
* @return {Vector3} A reference to this vector.
*/
multiply( v ) {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
return this;
}
/**
* Multiplies the given scalar value with all components of this instance.
*
* @param {number} scalar - The scalar to multiply.
* @return {Vector3} A reference to this vector.
*/
multiplyScalar( scalar ) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
return this;
}
/**
* Multiplies the given vectors and stores the result in this instance.
*
* @param {Vector3} a - The first vector.
* @param {Vector3} b - The second vector.
* @return {Vector3} A reference to this vector.
*/
multiplyVectors( a, b ) {
this.x = a.x * b.x;
this.y = a.y * b.y;
this.z = a.z * b.z;
return this;
}
/**
* Applies the given Euler rotation to this vector.
*
* @param {Euler} euler - The Euler angles.
* @return {Vector3} A reference to this vector.
*/
applyEuler( euler ) {
return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) );
}
/**
* Applies a rotation specified by an axis and an angle to this vector.
*
* @param {Vector3} axis - A normalized vector representing the rotation axis.
* @param {number} angle - The angle in radians.
* @return {Vector3} A reference to this vector.
*/
applyAxisAngle( axis, angle ) {
return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) );
}
/**
* Multiplies this vector with the given 3x3 matrix.
*
* @param {Matrix3} m - The 3x3 matrix.
* @return {Vector3} A reference to this vector.
*/
applyMatrix3( m ) {
const x = this.x, y = this.y, z = this.z;
const e = m.elements;
this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
return this;
}
/**
* Multiplies this vector by the given normal matrix and normalizes
* the result.
*
* @param {Matrix3} m - The normal matrix.
* @return {Vector3} A reference to this vector.
*/
applyNormalMatrix( m ) {
return this.applyMatrix3( m ).normalize();
}
/**
* Multiplies this vector (with an implicit 1 in the 4th dimension) by m, and
* divides by perspective.
*
* @param {Matrix4} m - The matrix to apply.
* @return {Vector3} A reference to this vector.
*/
applyMatrix4( m ) {
const x = this.x, y = this.y, z = this.z;
const e = m.elements;
const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
return this;
}
/**
* Applies the given Quaternion to this vector.
*
* @param {Quaternion} q - The Quaternion.
* @return {Vector3} A reference to this vector.
*/
applyQuaternion( q ) {
// quaternion q is assumed to have unit length
const vx = this.x, vy = this.y, vz = this.z;
const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
// t = 2 * cross( q.xyz, v );
const tx = 2 * ( qy * vz - qz * vy );
const ty = 2 * ( qz * vx - qx * vz );
const tz = 2 * ( qx * vy - qy * vx );
// v + q.w * t + cross( q.xyz, t );
this.x = vx + qw * tx + qy * tz - qz * ty;
this.y = vy + qw * ty + qz * tx - qx * tz;
this.z = vz + qw * tz + qx * ty - qy * tx;
return this;
}
/**
* Projects this vector from world space into the camera's normalized
* device coordinate (NDC) space.
*
* @param {Camera} camera - The camera.
* @return {Vector3} A reference to this vector.
*/
project( camera ) {
return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
}
/**
* Unprojects this vector from the camera's normalized device coordinate (NDC)
* space into world space.
*
* @param {Camera} camera - The camera.
* @return {Vector3} A reference to this vector.
*/
unproject( camera ) {
return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld );
}
/**
* Transforms the direction of this vector by a matrix (the upper left 3 x 3
* subset of the given 4x4 matrix and then normalizes the result.
*
* @param {Matrix4} m - The matrix.
* @return {Vector3} A reference to this vector.
*/
transformDirection( m ) {
// input: THREE.Matrix4 affine matrix
// vector interpreted as a direction
const x = this.x, y = this.y, z = this.z;
const e = m.elements;
this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
return this.normalize();
}
/**
* Divides this instance by the given vector.
*
* @param {Vector3} v - The vector to divide.
* @return {Vector3} A reference to this vector.
*/
divide( v ) {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
return this;
}
/**
* Divides this vector by the given scalar.
*
* @param {number} scalar - The scalar to divide.
* @return {Vector3} A reference to this vector.
*/
divideScalar( scalar ) {
return this.multiplyScalar( 1 / scalar );
}
/**
* If this vector's x, y or z value is greater than the given vector's x, y or z
* value, replace that value with the corresponding min value.
*
* @param {Vector3} v - The vector.
* @return {Vector3} A reference to this vector.
*/
min( v ) {
this.x = Math.min( this.x, v.x );
this.y = Math.min( this.y, v.y );
this.z = Math.min( this.z, v.z );
return this;
}
/**
* If this vector's x, y or z value is less than the given vector's x, y or z
* value, replace that value with the corresponding max value.
*
* @param {Vector3} v - The vector.
* @return {Vector3} A reference to this vector.
*/
max( v ) {
this.x = Math.max( this.x, v.x );
this.y = Math.max( this.y, v.y );
this.z = Math.max( this.z, v.z );
return this;
}
/**
* If this vector's x, y or z value is greater than the max vector's x, y or z
* value, it is replaced by the corresponding value.
* If this vector's x, y or z value is less than the min vector's x, y or z value,
* it is replaced by the corresponding value.
*
* @param {Vector3} min - The minimum x, y and z values.
* @param {Vector3} max - The maximum x, y and z values in the desired range.
* @return {Vector3} A reference to this vector.
*/
clamp( min, max ) {
// assumes min < max, componentwise
this.x = clamp( this.x, min.x, max.x );
this.y = clamp( this.y, min.y, max.y );
this.z = clamp( this.z, min.z, max.z );
return this;
}
/**
* If this vector's x, y or z values are greater than the max value, they are
* replaced by the max value.
* If this vector's x, y or z values are less than the min value, they are
* replaced by the min value.
*
* @param {number} minVal - The minimum value the components will be clamped to.
* @param {number} maxVal - The maximum value the components will be clamped to.
* @return {Vector3} A reference to this vector.
*/
clampScalar( minVal, maxVal ) {
this.x = clamp( this.x, minVal, maxVal );
this.y = clamp( this.y, minVal, maxVal );
this.z = clamp( this.z, minVal, maxVal );
return this;
}
/**
* If this vector's length is greater than the max value, it is replaced by
* the max value.
* If this vector's length is less than the min value, it is replaced by the
* min value.
*
* @param {number} min - The minimum value the vector length will be clamped to.
* @param {number} max - The maximum value the vector length will be clamped to.
* @return {Vector3} A reference to this vector.
*/
clampLength( min, max ) {
const length = this.length();
return this.divideScalar( length || 1 ).multiplyScalar( clamp( length, min, max ) );
}
/**
* The components of this vector are rounded down to the nearest integer value.
*
* @return {Vector3} A reference to this vector.
*/
floor() {
this.x = Math.floor( this.x );
this.y = Math.floor( this.y );
this.z = Math.floor( this.z );
return this;
}
/**
* The components of this vector are rounded up to the nearest integer value.
*
* @return {Vector3} A reference to this vector.
*/
ceil() {
this.x = Math.ceil( this.x );
this.y = Math.ceil( this.y );
this.z = Math.ceil( this.z );
return this;
}
/**
* The components of this vector are rounded to the nearest integer value
*
* @return {Vector3} A reference to this vector.
*/
round() {
this.x = Math.round( this.x );
this.y = Math.round( this.y );
this.z = Math.round( this.z );
return this;
}
/**
* The components of this vector are rounded towards zero (up if negative,
* down if positive) to an integer value.
*
* @return {Vector3} A reference to this vector.
*/
roundToZero() {
this.x = Math.trunc( this.x );
this.y = Math.trunc( this.y );
this.z = Math.trunc( this.z );
return this;
}
/**
* Inverts this vector - i.e. sets x = -x, y = -y and z = -z.
*
* @return {Vector3} A reference to this vector.
*/
negate() {
this.x = - this.x;
this.y = - this.y;
this.z = - this.z;
return this;
}
/**
* Calculates the dot product of the given vector with this instance.
*
* @param {Vector3} v - The vector to compute the dot product with.
* @return {number} The result of the dot product.
*/
dot( v ) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
// TODO lengthSquared?
/**
* Computes the square of the Euclidean length (straight-line length) from
* (0, 0, 0) to (x, y, z). If you are comparing the lengths of vectors, you should
* compare the length squared instead as it is slightly more efficient to calculate.
*
* @return {number} The square length of this vector.
*/
lengthSq() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
/**
* Computes the Euclidean length (straight-line length) from (0, 0, 0) to (x, y, z).
*
* @return {number} The length of this vector.
*/
length() {
return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
}
/**
* Computes the Manhattan length of this vector.
*
* @return {number} The length of this vector.
*/
manhattanLength() {
return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
}
/**
* Converts this vector to a unit vector - that is, sets it equal to a vector
* with the same direction as this one, but with a vector length of `1`.
*
* @return {Vector3} A reference to this vector.
*/
normalize() {
return this.divideScalar( this.length() || 1 );
}
/**
* Sets this vector to a vector with the same direction as this one, but
* with the specified length.
*
* @param {number} length - The new length of this vector.
* @return {Vector3} A reference to this vector.
*/
setLength( length ) {
return this.normalize().multiplyScalar( length );
}
/**
* Linearly interpolates between the given vector and this instance, where
* alpha is the percent distance along the line - alpha = 0 will be this
* vector, and alpha = 1 will be the given one.
*
* @param {Vector3} v - The vector to interpolate towards.
* @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
* @return {Vector3} A reference to this vector.
*/
lerp( v, alpha ) {
this.x += ( v.x - this.x ) * alpha;
this.y += ( v.y - this.y ) * alpha;
this.z += ( v.z - this.z ) * alpha;
return this;
}
/**
* Linearly interpolates between the given vectors, where alpha is the percent
* distance along the line - alpha = 0 will be first vector, and alpha = 1 will
* be the second one. The result is stored in this instance.
*
* @param {Vector3} v1 - The first vector.
* @param {Vector3} v2 - The second vector.
* @param {number} alpha - The interpolation factor, typically in the closed interval `[0, 1]`.
* @return {Vector3} A reference to this vector.
*/
lerpVectors( v1, v2, alpha ) {
this.x = v1.x + ( v2.x - v1.x ) * alpha;
this.y = v1.y + ( v2.y - v1.y ) * alpha;
this.z = v1.z + ( v2.z - v1.z ) * alpha;
return this;
}
/**
* Calculates the cross product of the given vector with this instance.
*
* @param {Vector3} v - The vector to compute the cross product with.
* @return {Vector3} The result of the cross product.
*/
cross( v ) {
return this.crossVectors( this, v );
}
/**
* Calculates the cross product of the given vectors and stores the result
* in this instance.
*
* @param {Vector3} a - The first vector.
* @param {Vector3} b - The second vector.
* @return {Vector3} A reference to this vector.
*/
crossVectors( a, b ) {
const ax = a.x, ay = a.y, az = a.z;
const bx = b.x, by = b.y, bz = b.z;
this.x = ay * bz - az * by;
this.y = az * bx - ax * bz;
this.z = ax * by - ay * bx;
return this;
}
/**
* Projects this vector onto the given one.
*
* @param {Vector3} v - The vector to project to.
* @return {Vector3} A reference to this vector.
*/
projectOnVector( v ) {
const denominator = v.lengthSq();
if ( denominator === 0 ) return this.set( 0, 0, 0 );
const scalar = v.dot( this ) / denominator;
return this.copy( v ).multiplyScalar( scalar );
}
/**
* Projects this vector onto a plane by subtracting this
* vector projected onto the plane's normal from this vector.
*
* @param {Vector3} planeNormal - The plane normal.
* @return {Vector3} A reference to this vector.
*/
projectOnPlane( planeNormal ) {
_vector$c.copy( this ).projectOnVector( planeNormal );
return this.sub( _vector$c );
}
/**
* Reflects this vector off a plane orthogonal to the given normal vector.
*
* @param {Vector3} normal - The (normalized) normal vector.
* @return {Vector3} A reference to this vector.
*/
reflect( normal ) {
return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
}
/**
* Returns the angle between the given vector and this instance in radians.
*
* @param {Vector3} v - The vector to compute the angle with.
* @return {number} The angle in radians.
*/
angleTo( v ) {
const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
if ( denominator === 0 ) return Math.PI / 2;
const theta = this.dot( v ) / denominator;
// clamp, to handle numerical problems
return Math.acos( clamp( theta, -1, 1 ) );
}
/**
* Computes the distance from the given vector to this instance.
*
* @param {Vector3} v - The vector to compute the distance to.
* @return {number} The distance.
*/
distanceTo( v ) {
return Math.sqrt( this.distanceToSquared( v ) );
}
/**
* Computes the squared distance from the given vector to this instance.
* If you are just comparing the distance with another distance, you should compare
* the distance squared instead as it is slightly more efficient to calculate.
*
* @param {Vector3} v - The vector to compute the squared distance to.
* @return {number} The squared distance.
*/
distanceToSquared( v ) {
const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
return dx * dx + dy * dy + dz * dz;
}
/**
* Computes the Manhattan distance from the given vector to this instance.
*
* @param {Vector3} v - The vector to compute the Manhattan distance to.
* @return {number} The Manhattan distance.
*/
manhattanDistanceTo( v ) {
return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
}
/**
* Sets the vector components from the given spherical coordinates.
*
* @param {Spherical} s - The spherical coordinates.
* @return {Vector3} A reference to this vector.
*/
setFromSpherical( s ) {
return this.setFromSphericalCoords( s.radius, s.phi, s.theta );
}
/**
* Sets the vector components from the given spherical coordinates.
*
* @param {number} radius - The radius.
* @param {number} phi - The phi angle in radians.
* @param {number} theta - The theta angle in radians.
* @return {Vector3} A reference to this vector.
*/
setFromSphericalCoords( radius, phi, theta ) {
const sinPhiRadius = Math.sin( phi ) * radius;
this.x = sinPhiRadius * Math.sin( theta );
this.y = Math.cos( phi ) * radius;
this.z = sinPhiRadius * Math.cos( theta );
return this;
}
/**
* Sets the vector components from the given cylindrical coordinates.
*
* @param {Cylindrical} c - The cylindrical coordinates.
* @return {Vector3} A reference to this vector.
*/
setFromCylindrical( c ) {
return this.setFromCylindricalCoords( c.radius, c.theta, c.y );
}
/**
* Sets the vector components from the given cylindrical coordinates.
*
* @param {number} radius - The radius.
* @param {number} theta - The theta angle in radians.
* @param {number} y - The y value.
* @return {Vector3} A reference to this vector.
*/
setFromCylindricalCoords( radius, theta, y ) {
this.x = radius * Math.sin( theta );
this.y = y;
this.z = radius * Math.cos( theta );
return this;
}
/**
* Sets the vector components to the position elements of the
* given transformation matrix.
*
* @param {Matrix4} m - The 4x4 matrix.
* @return {Vector3} A reference to this vector.
*/
setFromMatrixPosition( m ) {
const e = m.elements;
this.x = e[ 12 ];
this.y = e[ 13 ];
this.z = e[ 14 ];
return this;
}
/**
* Sets the vector components to the scale elements of the
* given transformation matrix.
*
* @param {Matrix4} m - The 4x4 matrix.
* @return {Vector3} A reference to this vector.
*/
setFromMatrixScale( m ) {
const sx = this.setFromMatrixColumn( m, 0 ).length();
const sy = this.setFromMatrixColumn( m, 1 ).length();
const sz = this.setFromMatrixColumn( m, 2 ).length();
this.x = sx;
this.y = sy;
this.z = sz;
return this;
}
/**
* Sets the vector components from the specified matrix column.
*
* @param {Matrix4} m - The 4x4 matrix.
* @param {number} index - The column index.
* @return {Vector3} A reference to this vector.
*/
setFromMatrixColumn( m, index ) {
return this.fromArray( m.elements, index * 4 );
}
/**
* Sets the vector components from the specified matrix column.
*
* @param {Matrix3} m - The 3x3 matrix.
* @param {number} index - The column index.
* @return {Vector3} A reference to this vector.
*/
setFromMatrix3Column( m, index ) {
return this.fromArray( m.elements, index * 3 );
}
/**
* Sets the vector components from the given Euler angles.
*
* @param {Euler} e - The Euler angles to set.
* @return {Vector3} A reference to this vector.
*/
setFromEuler( e ) {
this.x = e._x;
this.y = e._y;
this.z = e._z;
return this;
}
/**
* Sets the vector components from the RGB components of the
* given color.
*
* @param {Color} c - The color to set.
* @return {Vector3} A reference to this vector.
*/
setFromColor( c ) {
this.x = c.r;
this.y = c.g;
this.z = c.b;
return this;
}
/**
* Returns `true` if this vector is equal with the given one.
*
* @param {Vector3} v - The vector to test for equality.
* @return {boolean} Whether this vector is equal with the given one.
*/
equals( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
}
/**
* Sets this vector's x value to be `array[ offset ]`, y value to be `array[ offset + 1 ]`
* and z value to be `array[ offset + 2 ]`.
*
* @param {Array} array - An array holding the vector component values.
* @param {number} [offset=0] - The offset into the array.
* @return {Vector3} A reference to this vector.
*/
fromArray( array, offset = 0 ) {
this.x = array[ offset ];
this.y = array[ offset + 1 ];
this.z = array[ offset + 2 ];
return this;
}
/**
* Writes the components of this vector to the given array. If no array is provided,
* the method returns a new instance.
*
* @param {Array} [array=[]] - The target array holding the vector components.
* @param {number} [offset=0] - Index of the first element in the array.
* @return {Array} The vector components.
*/
toArray( array = [], offset = 0 ) {
array[ offset ] = this.x;
array[ offset + 1 ] = this.y;
array[ offset + 2 ] = this.z;
return array;
}
/**
* Sets the components of this vector from the given buffer attribute.
*
* @param {BufferAttribute} attribute - The buffer attribute holding vector data.
* @param {number} index - The index into the attribute.
* @return {Vector3} A reference to this vector.
*/
fromBufferAttribute( attribute, index ) {
this.x = attribute.getX( index );
this.y = attribute.getY( index );
this.z = attribute.getZ( index );
return this;
}
/**
* Sets each component of this vector to a pseudo-random value between `0` and
* `1`, excluding `1`.
*
* @return {Vector3} A reference to this vector.
*/
random() {
this.x = Math.random();
this.y = Math.random();
this.z = Math.random();
return this;
}
/**
* Sets this vector to a uniformly random point on a unit sphere.
*
* @return {Vector3} A reference to this vector.
*/
randomDirection() {
// https://mathworld.wolfram.com/SpherePointPicking.html
const theta = Math.random() * Math.PI * 2;
const u = Math.random() * 2 - 1;
const c = Math.sqrt( 1 - u * u );
this.x = c * Math.cos( theta );
this.y = u;
this.z = c * Math.sin( theta );
return this;
}
*[ Symbol.iterator ]() {
yield this.x;
yield this.y;
yield this.z;
}
}
const _vector$c = /*@__PURE__*/ new Vector3$1();
const _quaternion$4 = /*@__PURE__*/ new Quaternion();
/**
* Represents a 3x3 matrix.
*
* A Note on Row-Major and Column-Major Ordering:
*
* The constructor and {@link Matrix3#set} method take arguments in
* [row-major]{@link https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order}
* order, while internally they are stored in the {@link Matrix3#elements} array in column-major order.
* This means that calling:
* ```js
* const m = new THREE.Matrix();
* m.set( 11, 12, 13,
* 21, 22, 23,
* 31, 32, 33 );
* ```
* will result in the elements array containing:
* ```js
* m.elements = [ 11, 21, 31,
* 12, 22, 32,
* 13, 23, 33 ];
* ```
* and internally all calculations are performed using column-major ordering.
* However, as the actual ordering makes no difference mathematically and
* most people are used to thinking about matrices in row-major order, the
* three.js documentation shows matrices in row-major order. Just bear in
* mind that if you are reading the source code, you'll have to take the
* transpose of any matrices outlined here to make sense of the calculations.
*/
class Matrix3 {
/**
* Constructs a new 3x3 matrix. The arguments are supposed to be
* in row-major order. If no arguments are provided, the constructor
* initializes the matrix as an identity matrix.
*
* @param {number} [n11] - 1-1 matrix element.
* @param {number} [n12] - 1-2 matrix element.
* @param {number} [n13] - 1-3 matrix element.
* @param {number} [n21] - 2-1 matrix element.
* @param {number} [n22] - 2-2 matrix element.
* @param {number} [n23] - 2-3 matrix element.
* @param {number} [n31] - 3-1 matrix element.
* @param {number} [n32] - 3-2 matrix element.
* @param {number} [n33] - 3-3 matrix element.
*/
constructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
Matrix3.prototype.isMatrix3 = true;
/**
* A column-major list of matrix values.
*
* @type {Array}
*/
this.elements = [
1, 0, 0,
0, 1, 0,
0, 0, 1
];
if ( n11 !== undefined ) {
this.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 );
}
}
/**
* Sets the elements of the matrix.The arguments are supposed to be
* in row-major order.
*
* @param {number} [n11] - 1-1 matrix element.
* @param {number} [n12] - 1-2 matrix element.
* @param {number} [n13] - 1-3 matrix element.
* @param {number} [n21] - 2-1 matrix element.
* @param {number} [n22] - 2-2 matrix element.
* @param {number} [n23] - 2-3 matrix element.
* @param {number} [n31] - 3-1 matrix element.
* @param {number} [n32] - 3-2 matrix element.
* @param {number} [n33] - 3-3 matrix element.
* @return {Matrix3} A reference to this matrix.
*/
set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
const te = this.elements;
te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
return this;
}
/**
* Sets this matrix to the 3x3 identity matrix.
*
* @return {Matrix3} A reference to this matrix.
*/
identity() {
this.set(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
return this;
}
/**
* Copies the values of the given matrix to this instance.
*
* @param {Matrix3} m - The matrix to copy.
* @return {Matrix3} A reference to this matrix.
*/
copy( m ) {
const te = this.elements;
const me = m.elements;
te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
return this;
}
/**
* Extracts the basis of this matrix into the three axis vectors provided.
*
* @param {Vector3} xAxis - The basis's x axis.
* @param {Vector3} yAxis - The basis's y axis.
* @param {Vector3} zAxis - The basis's z axis.
* @return {Matrix3} A reference to this matrix.
*/
extractBasis( xAxis, yAxis, zAxis ) {
xAxis.setFromMatrix3Column( this, 0 );
yAxis.setFromMatrix3Column( this, 1 );
zAxis.setFromMatrix3Column( this, 2 );
return this;
}
/**
* Set this matrix to the upper 3x3 matrix of the given 4x4 matrix.
*
* @param {Matrix4} m - The 4x4 matrix.
* @return {Matrix3} A reference to this matrix.
*/
setFromMatrix4( m ) {
const me = m.elements;
this.set(
me[ 0 ], me[ 4 ], me[ 8 ],
me[ 1 ], me[ 5 ], me[ 9 ],
me[ 2 ], me[ 6 ], me[ 10 ]
);
return this;
}
/**
* Post-multiplies this matrix by the given 3x3 matrix.
*
* @param {Matrix3} m - The matrix to multiply with.
* @return {Matrix3} A reference to this matrix.
*/
multiply( m ) {
return this.multiplyMatrices( this, m );
}
/**
* Pre-multiplies this matrix by the given 3x3 matrix.
*
* @param {Matrix3} m - The matrix to multiply with.
* @return {Matrix3} A reference to this matrix.
*/
premultiply( m ) {
return this.multiplyMatrices( m, this );
}
/**
* Multiples the given 3x3 matrices and stores the result
* in this matrix.
*
* @param {Matrix3} a - The first matrix.
* @param {Matrix3} b - The second matrix.
* @return {Matrix3} A reference to this matrix.
*/
multiplyMatrices( a, b ) {
const ae = a.elements;
const be = b.elements;
const te = this.elements;
const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
return this;
}
/**
* Multiplies every component of the matrix by the given scalar.
*
* @param {number} s - The scalar.
* @return {Matrix3} A reference to this matrix.
*/
multiplyScalar( s ) {
const te = this.elements;
te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
return this;
}
/**
* Computes and returns the determinant of this matrix.
*
* @return {number} The determinant.
*/
determinant() {
const te = this.elements;
const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
}
/**
* Inverts this matrix, using the [analytic method]{@link https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution}.
* You can not invert with a determinant of zero. If you attempt this, the method produces
* a zero matrix instead.
*
* @return {Matrix3} A reference to this matrix.
*/
invert() {
const te = this.elements,
n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ],
n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ],
n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ],
t11 = n33 * n22 - n32 * n23,
t12 = n32 * n13 - n33 * n12,
t13 = n23 * n12 - n22 * n13,
det = n11 * t11 + n21 * t12 + n31 * t13;
if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
const detInv = 1 / det;
te[ 0 ] = t11 * detInv;
te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
te[ 3 ] = t12 * detInv;
te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
te[ 6 ] = t13 * detInv;
te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
return this;
}
/**
* Transposes this matrix in place.
*
* @return {Matrix3} A reference to this matrix.
*/
transpose() {
let tmp;
const m = this.elements;
tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
return this;
}
/**
* Computes the normal matrix which is the inverse transpose of the upper
* left 3x3 portion of the given 4x4 matrix.
*
* @param {Matrix4} matrix4 - The 4x4 matrix.
* @return {Matrix3} A reference to this matrix.
*/
getNormalMatrix( matrix4 ) {
return this.setFromMatrix4( matrix4 ).invert().transpose();
}
/**
* Transposes this matrix into the supplied array, and returns itself unchanged.
*
* @param {Array} r - An array to store the transposed matrix elements.
* @return {Matrix3} A reference to this matrix.
*/
transposeIntoArray( r ) {
const m = this.elements;
r[ 0 ] = m[ 0 ];
r[ 1 ] = m[ 3 ];
r[ 2 ] = m[ 6 ];
r[ 3 ] = m[ 1 ];
r[ 4 ] = m[ 4 ];
r[ 5 ] = m[ 7 ];
r[ 6 ] = m[ 2 ];
r[ 7 ] = m[ 5 ];
r[ 8 ] = m[ 8 ];
return this;
}
/**
* Sets the UV transform matrix from offset, repeat, rotation, and center.
*
* @param {number} tx - Offset x.
* @param {number} ty - Offset y.
* @param {number} sx - Repeat x.
* @param {number} sy - Repeat y.
* @param {number} rotation - Rotation, in radians. Positive values rotate counterclockwise.
* @param {number} cx - Center x of rotation.
* @param {number} cy - Center y of rotation
* @return {Matrix3} A reference to this matrix.
*/
setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {
const c = Math.cos( rotation );
const s = Math.sin( rotation );
this.set(
sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
- sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
0, 0, 1
);
return this;
}
/**
* Scales this matrix with the given scalar values.
*
* @param {number} sx - The amount to scale in the X axis.
* @param {number} sy - The amount to scale in the Y axis.
* @return {Matrix3} A reference to this matrix.
*/
scale( sx, sy ) {
this.premultiply( _m3.makeScale( sx, sy ) );
return this;
}
/**
* Rotates this matrix by the given angle.
*
* @param {number} theta - The rotation in radians.
* @return {Matrix3} A reference to this matrix.
*/
rotate( theta ) {
this.premultiply( _m3.makeRotation( - theta ) );
return this;
}
/**
* Translates this matrix by the given scalar values.
*
* @param {number} tx - The amount to translate in the X axis.
* @param {number} ty - The amount to translate in the Y axis.
* @return {Matrix3} A reference to this matrix.
*/
translate( tx, ty ) {
this.premultiply( _m3.makeTranslation( tx, ty ) );
return this;
}
// for 2D Transforms
/**
* Sets this matrix as a 2D translation transform.
*
* @param {number|Vector2} x - The amount to translate in the X axis or alternatively a translation vector.
* @param {number} y - The amount to translate in the Y axis.
* @return {Matrix3} A reference to this matrix.
*/
makeTranslation( x, y ) {
if ( x.isVector2 ) {
this.set(
1, 0, x.x,
0, 1, x.y,
0, 0, 1
);
} else {
this.set(
1, 0, x,
0, 1, y,
0, 0, 1
);
}
return this;
}
/**
* Sets this matrix as a 2D rotational transformation.
*
* @param {number} theta - The rotation in radians.
* @return {Matrix3} A reference to this matrix.
*/
makeRotation( theta ) {
// counterclockwise
const c = Math.cos( theta );
const s = Math.sin( theta );
this.set(
c, - s, 0,
s, c, 0,
0, 0, 1
);
return this;
}
/**
* Sets this matrix as a 2D scale transform.
*
* @param {number} x - The amount to scale in the X axis.
* @param {number} y - The amount to scale in the Y axis.
* @return {Matrix3} A reference to this matrix.
*/
makeScale( x, y ) {
this.set(
x, 0, 0,
0, y, 0,
0, 0, 1
);
return this;
}
/**
* Returns `true` if this matrix is equal with the given one.
*
* @param {Matrix3} matrix - The matrix to test for equality.
* @return {boolean} Whether this matrix is equal with the given one.
*/
equals( matrix ) {
const te = this.elements;
const me = matrix.elements;
for ( let i = 0; i < 9; i ++ ) {
if ( te[ i ] !== me[ i ] ) return false;
}
return true;
}
/**
* Sets the elements of the matrix from the given array.
*
* @param {Array} array - The matrix elements in column-major order.
* @param {number} [offset=0] - Index of the first element in the array.
* @return {Matrix3} A reference to this matrix.
*/
fromArray( array, offset = 0 ) {
for ( let i = 0; i < 9; i ++ ) {
this.elements[ i ] = array[ i + offset ];
}
return this;
}
/**
* Writes the elements of this matrix to the given array. If no array is provided,
* the method returns a new instance.
*
* @param {Array} [array=[]] - The target array holding the matrix elements in column-major order.
* @param {number} [offset=0] - Index of the first element in the array.
* @return {Array} The matrix elements in column-major order.
*/
toArray( array = [], offset = 0 ) {
const te = this.elements;
array[ offset ] = te[ 0 ];
array[ offset + 1 ] = te[ 1 ];
array[ offset + 2 ] = te[ 2 ];
array[ offset + 3 ] = te[ 3 ];
array[ offset + 4 ] = te[ 4 ];
array[ offset + 5 ] = te[ 5 ];
array[ offset + 6 ] = te[ 6 ];
array[ offset + 7 ] = te[ 7 ];
array[ offset + 8 ] = te[ 8 ];
return array;
}
/**
* Returns a matrix with copied values from this instance.
*
* @return {Matrix3} A clone of this instance.
*/
clone() {
return new this.constructor().fromArray( this.elements );
}
}
const _m3 = /*@__PURE__*/ new Matrix3();
function arrayNeedsUint32( array ) {
// assumes larger values usually on last
for ( let i = array.length - 1; i >= 0; -- i ) {
if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565
}
return false;
}
function createElementNS( name ) {
return document.createElementNS( 'http://www.w3.org/1999/xhtml', name );
}
function createCanvasElement() {
const canvas = createElementNS( 'canvas' );
canvas.style.display = 'block';
return canvas;
}
const _cache = {};
function warnOnce( message ) {
if ( message in _cache ) return;
_cache[ message ] = true;
console.warn( message );
}
function probeAsync( gl, sync, interval ) {
return new Promise( function ( resolve, reject ) {
function probe() {
switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) {
case gl.WAIT_FAILED:
reject();
break;
case gl.TIMEOUT_EXPIRED:
setTimeout( probe, interval );
break;
default:
resolve();
}
}
setTimeout( probe, interval );
} );
}
function toNormalizedProjectionMatrix( projectionMatrix ) {
const m = projectionMatrix.elements;
// Convert [-1, 1] to [0, 1] projection matrix
m[ 2 ] = 0.5 * m[ 2 ] + 0.5 * m[ 3 ];
m[ 6 ] = 0.5 * m[ 6 ] + 0.5 * m[ 7 ];
m[ 10 ] = 0.5 * m[ 10 ] + 0.5 * m[ 11 ];
m[ 14 ] = 0.5 * m[ 14 ] + 0.5 * m[ 15 ];
}
function toReversedProjectionMatrix( projectionMatrix ) {
const m = projectionMatrix.elements;
const isPerspectiveMatrix = m[ 11 ] === -1;
// Reverse [0, 1] projection matrix
if ( isPerspectiveMatrix ) {
m[ 10 ] = - m[ 10 ] - 1;
m[ 14 ] = - m[ 14 ];
} else {
m[ 10 ] = - m[ 10 ];
m[ 14 ] = - m[ 14 ] + 1;
}
}
const LINEAR_REC709_TO_XYZ = /*@__PURE__*/ new Matrix3().set(
0.4123908, 0.3575843, 0.1804808,
0.2126390, 0.7151687, 0.0721923,
0.0193308, 0.1191948, 0.9505322
);
const XYZ_TO_LINEAR_REC709 = /*@__PURE__*/ new Matrix3().set(
3.2409699, -1.5373832, -0.4986108,
-0.9692436, 1.8759675, 0.0415551,
0.0556301, -0.203977, 1.0569715
);
function createColorManagement() {
const ColorManagement = {
enabled: true,
workingColorSpace: LinearSRGBColorSpace,
/**
* Implementations of supported color spaces.
*
* Required:
* - primaries: chromaticity coordinates [ rx ry gx gy bx by ]
* - whitePoint: reference white [ x y ]
* - transfer: transfer function (pre-defined)
* - toXYZ: Matrix3 RGB to XYZ transform
* - fromXYZ: Matrix3 XYZ to RGB transform
* - luminanceCoefficients: RGB luminance coefficients
*
* Optional:
* - outputColorSpaceConfig: { drawingBufferColorSpace: ColorSpace }
* - workingColorSpaceConfig: { unpackColorSpace: ColorSpace }
*
* Reference:
* - https://www.russellcottrell.com/photo/matrixCalculator.htm
*/
spaces: {},
convert: function ( color, sourceColorSpace, targetColorSpace ) {
if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) {
return color;
}
if ( this.spaces[ sourceColorSpace ].transfer === SRGBTransfer ) {
color.r = SRGBToLinear( color.r );
color.g = SRGBToLinear( color.g );
color.b = SRGBToLinear( color.b );
}
if ( this.spaces[ sourceColorSpace ].primaries !== this.spaces[ targetColorSpace ].primaries ) {
color.applyMatrix3( this.spaces[ sourceColorSpace ].toXYZ );
color.applyMatrix3( this.spaces[ targetColorSpace ].fromXYZ );
}
if ( this.spaces[ targetColorSpace ].transfer === SRGBTransfer ) {
color.r = LinearToSRGB( color.r );
color.g = LinearToSRGB( color.g );
color.b = LinearToSRGB( color.b );
}
return color;
},
workingToColorSpace: function ( color, targetColorSpace ) {
return this.convert( color, this.workingColorSpace, targetColorSpace );
},
colorSpaceToWorking: function ( color, sourceColorSpace ) {
return this.convert( color, sourceColorSpace, this.workingColorSpace );
},
getPrimaries: function ( colorSpace ) {
return this.spaces[ colorSpace ].primaries;
},
getTransfer: function ( colorSpace ) {
if ( colorSpace === NoColorSpace ) return LinearTransfer;
return this.spaces[ colorSpace ].transfer;
},
getLuminanceCoefficients: function ( target, colorSpace = this.workingColorSpace ) {
return target.fromArray( this.spaces[ colorSpace ].luminanceCoefficients );
},
define: function ( colorSpaces ) {
Object.assign( this.spaces, colorSpaces );
},
// Internal APIs
_getMatrix: function ( targetMatrix, sourceColorSpace, targetColorSpace ) {
return targetMatrix
.copy( this.spaces[ sourceColorSpace ].toXYZ )
.multiply( this.spaces[ targetColorSpace ].fromXYZ );
},
_getDrawingBufferColorSpace: function ( colorSpace ) {
return this.spaces[ colorSpace ].outputColorSpaceConfig.drawingBufferColorSpace;
},
_getUnpackColorSpace: function ( colorSpace = this.workingColorSpace ) {
return this.spaces[ colorSpace ].workingColorSpaceConfig.unpackColorSpace;
},
// Deprecated
fromWorkingColorSpace: function ( color, targetColorSpace ) {
warnOnce( 'THREE.ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace().' ); // @deprecated, r177
return ColorManagement.workingToColorSpace( color, targetColorSpace );
},
toWorkingColorSpace: function ( color, sourceColorSpace ) {
warnOnce( 'THREE.ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking().' ); // @deprecated, r177
return ColorManagement.colorSpaceToWorking( color, sourceColorSpace );
},
};
/******************************************************************************
* sRGB definitions
*/
const REC709_PRIMARIES = [ 0.640, 0.330, 0.300, 0.600, 0.150, 0.060 ];
const REC709_LUMINANCE_COEFFICIENTS = [ 0.2126, 0.7152, 0.0722 ];
const D65 = [ 0.3127, 0.3290 ];
ColorManagement.define( {
[ LinearSRGBColorSpace ]: {
primaries: REC709_PRIMARIES,
whitePoint: D65,
transfer: LinearTransfer,
toXYZ: LINEAR_REC709_TO_XYZ,
fromXYZ: XYZ_TO_LINEAR_REC709,
luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
workingColorSpaceConfig: { unpackColorSpace: SRGBColorSpace },
outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace }
},
[ SRGBColorSpace ]: {
primaries: REC709_PRIMARIES,
whitePoint: D65,
transfer: SRGBTransfer,
toXYZ: LINEAR_REC709_TO_XYZ,
fromXYZ: XYZ_TO_LINEAR_REC709,
luminanceCoefficients: REC709_LUMINANCE_COEFFICIENTS,
outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace }
},
} );
return ColorManagement;
}
const ColorManagement = /*@__PURE__*/ createColorManagement();
function SRGBToLinear( c ) {
return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
}
function LinearToSRGB( c ) {
return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
}
let _canvas;
/**
* A class containing utility functions for images.
*
* @hideconstructor
*/
class ImageUtils {
/**
* Returns a data URI containing a representation of the given image.
*
* @param {(HTMLImageElement|HTMLCanvasElement)} image - The image object.
* @param {string} [type='image/png'] - Indicates the image format.
* @return {string} The data URI.
*/
static getDataURL( image, type = 'image/png' ) {
if ( /^data:/i.test( image.src ) ) {
return image.src;
}
if ( typeof HTMLCanvasElement === 'undefined' ) {
return image.src;
}
let canvas;
if ( image instanceof HTMLCanvasElement ) {
canvas = image;
} else {
if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' );
_canvas.width = image.width;
_canvas.height = image.height;
const context = _canvas.getContext( '2d' );
if ( image instanceof ImageData ) {
context.putImageData( image, 0, 0 );
} else {
context.drawImage( image, 0, 0, image.width, image.height );
}
canvas = _canvas;
}
return canvas.toDataURL( type );
}
/**
* Converts the given sRGB image data to linear color space.
*
* @param {(HTMLImageElement|HTMLCanvasElement|ImageBitmap|Object)} image - The image object.
* @return {HTMLCanvasElement|Object} The converted image.
*/
static sRGBToLinear( image ) {
if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
const canvas = createElementNS( 'canvas' );
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext( '2d' );
context.drawImage( image, 0, 0, image.width, image.height );
const imageData = context.getImageData( 0, 0, image.width, image.height );
const data = imageData.data;
for ( let i = 0; i < data.length; i ++ ) {
data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255;
}
context.putImageData( imageData, 0, 0 );
return canvas;
} else if ( image.data ) {
const data = image.data.slice( 0 );
for ( let i = 0; i < data.length; i ++ ) {
if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) {
data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 );
} else {
// assuming float
data[ i ] = SRGBToLinear( data[ i ] );
}
}
return {
data: data,
width: image.width,
height: image.height
};
} else {
console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' );
return image;
}
}
}
let _sourceId = 0;
/**
* Represents the data source of a texture.
*
* The main purpose of this class is to decouple the data definition from the texture
* definition so the same data can be used with multiple texture instances.
*/
class Source {
/**
* Constructs a new video texture.
*
* @param {any} [data=null] - The data definition of a texture.
*/
constructor( data = null ) {
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isSource = true;
/**
* The ID of the source.
*
* @name Source#id
* @type {number}
* @readonly
*/
Object.defineProperty( this, 'id', { value: _sourceId ++ } );
/**
* The UUID of the source.
*
* @type {string}
* @readonly
*/
this.uuid = generateUUID();
/**
* The data definition of a texture.
*
* @type {any}
*/
this.data = data;
/**
* This property is only relevant when {@link Source#needsUpdate} is set to `true` and
* provides more control on how texture data should be processed. When `dataReady` is set
* to `false`, the engine performs the memory allocation (if necessary) but does not transfer
* the data into the GPU memory.
*
* @type {boolean}
* @default true
*/
this.dataReady = true;
/**
* This starts at `0` and counts how many times {@link Source#needsUpdate} is set to `true`.
*
* @type {number}
* @readonly
* @default 0
*/
this.version = 0;
}
getSize( target ) {
const data = this.data;
if ( data instanceof HTMLVideoElement ) {
target.set( data.videoWidth, data.videoHeight );
} else if ( data !== null ) {
target.set( data.width, data.height, data.depth || 0 );
} else {
target.set( 0, 0, 0 );
}
return target;
}
/**
* When the property is set to `true`, the engine allocates the memory
* for the texture (if necessary) and triggers the actual texture upload
* to the GPU next time the source is used.
*
* @type {boolean}
* @default false
* @param {boolean} value
*/
set needsUpdate( value ) {
if ( value === true ) this.version ++;
}
/**
* Serializes the source into JSON.
*
* @param {?(Object|string)} meta - An optional value holding meta information about the serialization.
* @return {Object} A JSON object representing the serialized source.
* @see {@link ObjectLoader#parse}
*/
toJSON( meta ) {
const isRootObject = ( meta === undefined || typeof meta === 'string' );
if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) {
return meta.images[ this.uuid ];
}
const output = {
uuid: this.uuid,
url: ''
};
const data = this.data;
if ( data !== null ) {
let url;
if ( Array.isArray( data ) ) {
// cube texture
url = [];
for ( let i = 0, l = data.length; i < l; i ++ ) {
if ( data[ i ].isDataTexture ) {
url.push( serializeImage( data[ i ].image ) );
} else {
url.push( serializeImage( data[ i ] ) );
}
}
} else {
// texture
url = serializeImage( data );
}
output.url = url;
}
if ( ! isRootObject ) {
meta.images[ this.uuid ] = output;
}
return output;
}
}
function serializeImage( image ) {
if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
// default images
return ImageUtils.getDataURL( image );
} else {
if ( image.data ) {
// images of DataTexture
return {
data: Array.from( image.data ),
width: image.width,
height: image.height,
type: image.data.constructor.name
};
} else {
console.warn( 'THREE.Texture: Unable to serialize Texture.' );
return {};
}
}
}
let _textureId = 0;
const _tempVec3 = /*@__PURE__*/ new Vector3$1();
/**
* Base class for all textures.
*
* Note: After the initial use of a texture, its dimensions, format, and type
* cannot be changed. Instead, call {@link Texture#dispose} on the texture and instantiate a new one.
*
* @augments EventDispatcher
*/
class Texture$1 extends EventDispatcher {
/**
* Constructs a new texture.
*
* @param {?Object} [image=Texture.DEFAULT_IMAGE] - The image holding the texture data.
* @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping.
* @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value.
* @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value.
* @param {number} [magFilter=LinearFilter] - The mag filter value.
* @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value.
* @param {number} [format=RGBAFormat] - The texture format.
* @param {number} [type=UnsignedByteType] - The texture type.
* @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value.
* @param {string} [colorSpace=NoColorSpace] - The color space.
*/
constructor( image = Texture$1.DEFAULT_IMAGE, mapping = Texture$1.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter$1, minFilter = LinearMipmapLinearFilter$1, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture$1.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) {
super();
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isTexture = true;
/**
* The ID of the texture.
*
* @name Texture#id
* @type {number}
* @readonly
*/
Object.defineProperty( this, 'id', { value: _textureId ++ } );
/**
* The UUID of the material.
*
* @type {string}
* @readonly
*/
this.uuid = generateUUID();
/**
* The name of the material.
*
* @type {string}
*/
this.name = '';
/**
* The data definition of a texture. A reference to the data source can be
* shared across textures. This is often useful in context of spritesheets
* where multiple textures render the same data but with different texture
* transformations.
*
* @type {Source}
*/
this.source = new Source( image );
/**
* An array holding user-defined mipmaps.
*
* @type {Array