Przeglądaj źródła

Merge branch 'master' of https://github.com/molstar/molstar into forkdev

autin 5 lat temu
rodzic
commit
1a2e9eaa84
57 zmienionych plików z 401 dodań i 219 usunięć
  1. 1 1
      package-lock.json
  2. 3 4
      package.json
  3. 11 5
      src/examples/proteopedia-wrapper/index.html
  4. 19 21
      src/examples/proteopedia-wrapper/index.ts
  5. 5 3
      src/extensions/cellpack/model.ts
  6. 1 1
      src/mol-canvas3d/passes/postprocessing.ts
  7. 0 3
      src/mol-gl/renderable.ts
  8. 0 1
      src/mol-gl/renderable/direct-volume.ts
  9. 0 1
      src/mol-gl/renderable/image.ts
  10. 0 1
      src/mol-gl/renderable/lines.ts
  11. 0 1
      src/mol-gl/renderable/mesh.ts
  12. 0 1
      src/mol-gl/renderable/points.ts
  13. 0 1
      src/mol-gl/renderable/schema.ts
  14. 0 1
      src/mol-gl/renderable/spheres.ts
  15. 0 1
      src/mol-gl/renderable/text.ts
  16. 0 1
      src/mol-gl/renderable/texture-mesh.ts
  17. 35 33
      src/mol-gl/renderer.ts
  18. 1 1
      src/mol-gl/shader/chunks/assign-material-color.glsl.ts
  19. 0 1
      src/mol-gl/shader/chunks/common-frag-params.glsl.ts
  20. 0 3
      src/mol-gl/shader/direct-volume.frag.ts
  21. 8 12
      src/mol-gl/shader/image.frag.ts
  22. 4 4
      src/mol-io/reader/_spec/mol2.spec.ts
  23. 9 10
      src/mol-io/reader/mol2/parser.ts
  24. 1 0
      src/mol-io/reader/mol2/schema.d.ts
  25. 0 0
      src/mol-model-formats/format.ts
  26. 1 1
      src/mol-model-formats/structure/3dg.ts
  27. 1 1
      src/mol-model-formats/structure/basic/atomic.ts
  28. 1 1
      src/mol-model-formats/structure/basic/parser.ts
  29. 1 1
      src/mol-model-formats/structure/cif-core.ts
  30. 1 1
      src/mol-model-formats/structure/common/property.ts
  31. 1 1
      src/mol-model-formats/structure/cube.ts
  32. 1 1
      src/mol-model-formats/structure/gro.ts
  33. 1 1
      src/mol-model-formats/structure/mmcif.ts
  34. 1 1
      src/mol-model-formats/structure/mol.ts
  35. 98 0
      src/mol-model-formats/structure/mol2.ts
  36. 1 1
      src/mol-model-formats/structure/psf.ts
  37. 19 1
      src/mol-model-formats/volume/ccp4.ts
  38. 19 1
      src/mol-model-formats/volume/cube.ts
  39. 19 3
      src/mol-model-formats/volume/density-server.ts
  40. 19 3
      src/mol-model-formats/volume/dsn6.ts
  41. 19 1
      src/mol-model-formats/volume/dx.ts
  42. 1 1
      src/mol-model/structure/model/model.ts
  43. 1 1
      src/mol-model/structure/topology/topology.ts
  44. 4 1
      src/mol-model/volume/data.ts
  45. 6 0
      src/mol-model/volume/volume.ts
  46. 10 1
      src/mol-plugin-state/formats/trajectory.ts
  47. 26 14
      src/mol-plugin-state/formats/volume.ts
  48. 1 1
      src/mol-plugin-state/manager/snapshots.ts
  49. 21 0
      src/mol-plugin-state/transforms/model.ts
  50. 1 45
      src/mol-plugin-ui/skin/base/logo.scss
  51. 1 8
      src/mol-plugin-ui/viewport.tsx
  52. 7 4
      src/mol-plugin/context.ts
  53. 1 1
      src/mol-repr/structure/visual/label-text.ts
  54. 2 2
      src/mol-repr/structure/visual/util/common.ts
  55. 8 4
      src/mol-theme/size/physical.ts
  56. 1 2
      src/mol-util/data-source.ts
  57. 9 4
      src/mol-util/param-definition.ts

+ 1 - 1
package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "0.7.0-dev.10",
+  "version": "0.7.0-dev.13",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 3 - 4
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "0.7.0-dev.10",
+  "version": "0.7.0-dev.13",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -34,9 +34,8 @@
     "model-server-watch": "nodemon --watch lib lib/servers/servers/model/server.js",
     "volume-server-test": "node lib/servers/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
     "plugin-state": "node lib/servers/servers/plugin-state/index.js",
-    "preversion": "npm run test",
-    "postversion": "git push && git push --tags",
-    "prepublishOnly": "npm run test && npm run build"
+    "preversion": "npm run test && npm run build",
+    "postversion": "git push && git push --tags"
   },
   "files": [
     "lib/"

+ 11 - 5
src/examples/proteopedia-wrapper/index.html

@@ -92,6 +92,12 @@
             // var format = 'pdb';
             // var assemblyId = 'deposited';
 
+            function loadAndSnapshot(params) {
+                PluginWrapper.load(params).then(() => {
+                    setTimeout(() => snapshot = PluginWrapper.plugin.state.getSnapshot({ canvas3d: false /* do not save spinning state */ }), 500);
+                });
+            }
+
             var representationStyle = {
                 // sequence: { coloring: 'proteopedia-custom' }, // or just { }
                 hetGroups: { kind: 'ball-and-stick' }, // or 'spacefill
@@ -103,7 +109,7 @@
                 customColorList: CustomColors
             });
             PluginWrapper.setBackground(0xffffff);
-            PluginWrapper.load({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
+            loadAndSnapshot({ url: url, format: format, isBinary: isBinary, assemblyId: assemblyId, representationStyle: representationStyle });
             PluginWrapper.toggleSpin();
 
             PluginWrapper.events.modelInfo.subscribe(function (info) {
@@ -111,8 +117,8 @@
                 listHetGroups(info);
             });
 
-            addControl('Load Asym Unit', () => PluginWrapper.load({ url: url, format: format, isBinary }));
-            addControl('Load Assembly', () => PluginWrapper.load({ url: url, format: format, isBinary, assemblyId: assemblyId }));
+            addControl('Load Asym Unit', () => loadAndSnapshot({ url: url, format: format, isBinary }));
+            addControl('Load Assembly', () => loadAndSnapshot({ url: url, format: format, isBinary, assemblyId: assemblyId }));
 
             addSeparator();
 
@@ -185,10 +191,10 @@
                 PluginWrapper.snapshot.set(snapshot);
             });
             addControl('Download State', () => {
-                snapshot = PluginWrapper.snapshot.download('molj');
+                PluginWrapper.snapshot.download('molj');
             });
             addControl('Download Session', () => {
-                snapshot = PluginWrapper.snapshot.download('molx');
+                PluginWrapper.snapshot.download('molx');
             });
 
             ////////////////////////////////////////////////////////

+ 19 - 21
src/examples/proteopedia-wrapper/index.ts

@@ -5,35 +5,34 @@
  */
 
 import * as ReactDOM from 'react-dom';
+import { Canvas3DProps, DefaultCanvas3DParams } from '../../mol-canvas3d/canvas3d';
 import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
-import './index.html';
-import { PluginContext } from '../../mol-plugin/context';
-import { PluginCommands } from '../../mol-plugin/commands';
-import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { Color } from '../../mol-util/color';
-import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
 import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
-import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
-import { EvolutionaryConservation } from './annotation';
-import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
-import { RxEventHelper } from '../../mol-util/rx-event-helper';
-import { volumeStreamingControls } from './ui/controls';
+import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
+import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
+import { StateTransforms } from '../../mol-plugin-state/transforms';
+import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
+import { PluginCommands } from '../../mol-plugin/commands';
+import { PluginContext } from '../../mol-plugin/context';
 import { PluginState } from '../../mol-plugin/state';
-import { Scheduler } from '../../mol-task';
-import { createProteopediaCustomTheme } from './coloring';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
+import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
+import { Asset } from '../../mol-util/assets';
+import { Color } from '../../mol-util/color';
 import { ColorNames } from '../../mol-util/color/names';
-import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
-import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
-import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
-import { download } from '../../mol-util/download';
 import { getFormattedTime } from '../../mol-util/date';
-import { Asset } from '../../mol-util/assets';
+import { download } from '../../mol-util/download';
+import { RxEventHelper } from '../../mol-util/rx-event-helper';
+import { EvolutionaryConservation } from './annotation';
+import { createProteopediaCustomTheme } from './coloring';
+import { LoadParams, ModelInfo, RepresentationStyle, StateElements, SupportedFormats } from './helpers';
+import './index.html';
+import { volumeStreamingControls } from './ui/controls';
 require('../../mol-plugin-ui/skin/light.scss');
 
 class MolStarProteopediaWrapper {
     static VERSION_MAJOR = 5;
-    static VERSION_MINOR = 4;
+    static VERSION_MINOR = 5;
 
     private _ev = RxEventHelper.create();
 
@@ -233,7 +232,6 @@ class MolStarProteopediaWrapper {
         await this.updateStyle(representationStyle);
 
         this.loadedParams = { url, format, assemblyId };
-        Scheduler.setImmediate(() => PluginCommands.Camera.Reset(this.plugin, { }));
     }
 
     async updateStyle(style?: RepresentationStyle, partial?: boolean) {
@@ -407,7 +405,7 @@ class MolStarProteopediaWrapper {
             try {
                 const data = await this.plugin.runTask(this.plugin.fetch({ url, type: 'binary' }));
                 this.loadedParams = { ...this.emptyLoadedParams };
-                await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
+                return await this.plugin.managers.snapshot.open(new File([data], `state.${type}`));
             } catch (e) {
                 console.log(e);
             }

+ 5 - 3
src/extensions/cellpack/model.ts

@@ -69,11 +69,13 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
             }
         } else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
             if (surface){
-                const data = await getFromOPM(plugin, id, assetManager);
-                if (data.asset){
+                try {
+                    const data = await getFromOPM(plugin, id, assetManager);
                     assets.push(data.asset);
                     trajectory = await plugin.runTask(trajectoryFromPDB(data.pdb));
-                } else {
+                } catch (e) {
+                    // fallback to getFromPdb
+                    // console.error(e);
                     const { mmcif, asset } = await getFromPdb(plugin, id, assetManager);
                     assets.push(asset);
                     trajectory = await plugin.runTask(trajectoryFromMmCIF(mmcif));

+ 1 - 1
src/mol-canvas3d/passes/postprocessing.ts

@@ -59,7 +59,7 @@ export const PostprocessingParams = {
     outline: PD.MappedStatic('off', {
         on: PD.Group({
             scale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
-            threshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
+            threshold: PD.Numeric(0.8, { min: 0, max: 5, step: 0.01 }),
         }),
         off: PD.Group({})
     }, { cycle: true, description: 'Draw outline around 3D objects' })

+ 0 - 3
src/mol-gl/renderable.ts

@@ -44,9 +44,6 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
             if (values.uAlpha && values.alpha) {
                 ValueCell.updateIfChanged(values.uAlpha, clamp(values.alpha.ref.value * state.alphaFactor, 0, 1));
             }
-            if (values.uPickable) {
-                ValueCell.updateIfChanged(values.uPickable, state.pickable ? 1 : 0);
-            }
             renderItem.render(variant);
         },
         getProgram: (variant: GraphicsRenderVariant) => renderItem.getProgram(variant),

+ 0 - 1
src/mol-gl/renderable/direct-volume.ts

@@ -73,7 +73,6 @@ export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: Di
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...DirectVolumeSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0),
     };
     const shaderCode = DirectVolumeShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 0 - 1
src/mol-gl/renderable/image.ts

@@ -33,7 +33,6 @@ export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValu
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...ImageSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0),
     };
     const shaderCode = ImageShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 0 - 1
src/mol-gl/renderable/lines.ts

@@ -29,7 +29,6 @@ export function LinesRenderable(ctx: WebGLContext, id: number, values: LinesValu
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...LinesSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0)
     };
     const shaderCode = LinesShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 0 - 1
src/mol-gl/renderable/mesh.ts

@@ -28,7 +28,6 @@ export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...MeshSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0)
     };
     const shaderCode = MeshShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 0 - 1
src/mol-gl/renderable/points.ts

@@ -26,7 +26,6 @@ export function PointsRenderable(ctx: WebGLContext, id: number, values: PointsVa
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointsSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0)
     };
     const shaderCode = PointsShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 0 - 1
src/mol-gl/renderable/schema.ts

@@ -187,7 +187,6 @@ export type GlobalUniformValues = Values<GlobalUniformSchema> // { [k in keyof G
 
 export const InternalSchema = {
     uObjectId: UniformSpec('i'),
-    uPickable: UniformSpec('i', true),
 } as const;
 export type InternalSchema = typeof InternalSchema
 export type InternalValues = { [k in keyof InternalSchema]: ValueCell<any> }

+ 0 - 1
src/mol-gl/renderable/spheres.ts

@@ -29,7 +29,6 @@ export function SpheresRenderable(ctx: WebGLContext, id: number, values: Spheres
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...SpheresSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0)
     };
     const shaderCode = SpheresShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 0 - 1
src/mol-gl/renderable/text.ts

@@ -38,7 +38,6 @@ export function TextRenderable(ctx: WebGLContext, id: number, values: TextValues
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...TextSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0)
     };
     const shaderCode = TextShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 0 - 1
src/mol-gl/renderable/texture-mesh.ts

@@ -31,7 +31,6 @@ export function TextureMeshRenderable(ctx: WebGLContext, id: number, values: Tex
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...TextureMeshSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
-        uPickable: ValueCell.create(state.pickable ? 1 : 0)
     };
     const shaderCode = MeshShaderCode;
     const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);

+ 35 - 33
src/mol-gl/renderer.ts

@@ -173,51 +173,53 @@ namespace Renderer {
         let globalUniformsNeedUpdate = true;
 
         const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant) => {
+            if (!r.state.visible || (!r.state.pickable && variant[0] === 'p')) {
+                return;
+            }
+
             const program = r.getProgram(variant);
-            if (r.state.visible) {
-                if (state.currentProgramId !== program.id) {
-                    // console.log('new program')
-                    globalUniformsNeedUpdate = true;
-                    program.use();
-                }
+            if (state.currentProgramId !== program.id) {
+                // console.log('new program')
+                globalUniformsNeedUpdate = true;
+                program.use();
+            }
 
-                if (globalUniformsNeedUpdate) {
-                    // console.log('globalUniformsNeedUpdate')
-                    program.setUniforms(globalUniformList);
-                    globalUniformsNeedUpdate = false;
-                }
+            if (globalUniformsNeedUpdate) {
+                // console.log('globalUniformsNeedUpdate')
+                program.setUniforms(globalUniformList);
+                globalUniformsNeedUpdate = false;
+            }
 
-                if (r.values.dDoubleSided) {
-                    if (r.values.dDoubleSided.ref.value) {
-                        state.disable(gl.CULL_FACE);
-                    } else {
-                        state.enable(gl.CULL_FACE);
-                    }
-                } else {
-                    // webgl default
+            if (r.values.dDoubleSided) {
+                if (r.values.dDoubleSided.ref.value) {
                     state.disable(gl.CULL_FACE);
+                } else {
+                    state.enable(gl.CULL_FACE);
                 }
+            } else {
+                // webgl default
+                state.disable(gl.CULL_FACE);
+            }
 
-                if (r.values.dFlipSided) {
-                    if (r.values.dFlipSided.ref.value) {
-                        state.frontFace(gl.CW);
-                        state.cullFace(gl.FRONT);
-                    } else {
-                        state.frontFace(gl.CCW);
-                        state.cullFace(gl.BACK);
-                    }
+            if (r.values.dFlipSided) {
+                if (r.values.dFlipSided.ref.value) {
+                    state.frontFace(gl.CW);
+                    state.cullFace(gl.FRONT);
                 } else {
-                    // webgl default
                     state.frontFace(gl.CCW);
                     state.cullFace(gl.BACK);
                 }
+            } else {
+                // webgl default
+                state.frontFace(gl.CCW);
+                state.cullFace(gl.BACK);
+            }
 
-                if (variant === 'color') {
-                    state.depthMask(r.state.writeDepth);
-                }
-
-                r.render(variant);
+            if (variant === 'color') {
+                state.depthMask(r.state.writeDepth);
             }
+
+            r.render(variant);
         };
 
         const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => {

+ 1 - 1
src/mol-gl/shader/chunks/assign-material-color.glsl.ts

@@ -34,7 +34,7 @@ export default `
         if (ta < 0.99 && (ta < 0.01 || ta < at)) discard;
     #endif
 #elif defined(dRenderVariant_pick)
-    vec4 material = uPickable == 1 ? vColor : vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
+    vec4 material = vColor;
 #elif defined(dRenderVariant_depth)
     #ifdef enabledFragDepth
         vec4 material = packDepthToRGBA(gl_FragDepthEXT);

+ 0 - 1
src/mol-gl/shader/chunks/common-frag-params.glsl.ts

@@ -21,7 +21,6 @@ uniform vec3 uFogColor;
 
 uniform float uAlpha;
 uniform float uPickingAlphaThreshold;
-uniform int uPickable;
 uniform int uTransparentBackground;
 
 uniform float uInteriorDarkening;

+ 0 - 3
src/mol-gl/shader/direct-volume.frag.ts

@@ -28,7 +28,6 @@ uniform sampler2D tMarker;
 
 uniform float uAlpha;
 uniform float uPickingAlphaThreshold;
-uniform int uPickable;
 
 #if defined(dGridTexType_2d)
     precision highp sampler2D;
@@ -117,8 +116,6 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 viewDir) {
                 #if defined(dRenderVariant_pick)
                     if (uAlpha < uPickingAlphaThreshold)
                         discard; // ignore so the element below can be picked
-                    if (uPickable == 0)
-                        return vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
                 #endif
 
                 #if defined(dRenderVariant_pickObject)

+ 8 - 12
src/mol-gl/shader/image.frag.ts

@@ -99,18 +99,14 @@ void main() {
         if (imageData.a < 0.3)
             discard;
 
-        if (uPickable == 1) {
-            #if defined(dRenderVariant_pickObject)
-                gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
-            #elif defined(dRenderVariant_pickInstance)
-                gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
-            #elif defined(dRenderVariant_pickGroup)
-                float group = texture2D(tGroupTex, vUv).r;
-                gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
-            #endif
-        } else {
-            gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
-        }
+        #if defined(dRenderVariant_pickObject)
+            gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
+        #elif defined(dRenderVariant_pickInstance)
+            gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
+        #elif defined(dRenderVariant_pickGroup)
+            float group = texture2D(tGroupTex, vUv).r;
+            gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
+        #endif
     #elif defined(dRenderVariant_depth)
         if (imageData.a < 0.05)
             discard;

+ 4 - 4
src/mol-io/reader/_spec/mol2.spec.ts

@@ -1,5 +1,5 @@
 
-import Mol2 from '../mol2/parser';
+import { parseMol2 } from '../mol2/parser';
 
 const Mol2String = `@<TRIPOS>MOLECULE
 5816
@@ -246,7 +246,7 @@ GASTEIGER
 
 describe('mol2 reader', () => {
     it('basic', async () => {
-        const parsed =  await Mol2(Mol2String).run();
+        const parsed =  await parseMol2(Mol2String, '').run();
         if (parsed.isError) {
             throw new Error(parsed.message);
         }
@@ -297,7 +297,7 @@ describe('mol2 reader', () => {
     });
 
     it('multiblocks', async () => {
-        const parsed =  await Mol2(Mol2StringMultiBlocks).run();
+        const parsed =  await parseMol2(Mol2StringMultiBlocks, '').run();
         if (parsed.isError) {
             throw new Error(parsed.message);
         }
@@ -348,7 +348,7 @@ describe('mol2 reader', () => {
     });
 
     it('minimal', async () => {
-        const parsed =  await Mol2(Mol2StringMinimal).run();
+        const parsed =  await parseMol2(Mol2StringMinimal, '').run();
         if (parsed.isError) {
             throw new Error(parsed.message);
         }

+ 9 - 10
src/mol-io/reader/mol2/parser.ts

@@ -54,7 +54,7 @@ const reWhitespace = /\s+/g;
 function handleMolecule(state: State) {
     const { tokenizer, molecule } = state;
 
-    while (getTokenString(tokenizer) !== '@<TRIPOS>MOLECULE') {
+    while (getTokenString(tokenizer) !== '@<TRIPOS>MOLECULE' && tokenizer.position < tokenizer.data.length) {
         markLine(tokenizer);
     }
 
@@ -101,7 +101,7 @@ async function handleAtoms(state: State): Promise<Schema.Mol2Atoms> {
     let hasStatus_bit = false;
 
     // skip empty lines and '@<TRIPOS>ATOM'
-    while (getTokenString(tokenizer) !== '@<TRIPOS>ATOM') {
+    while (getTokenString(tokenizer) !== '@<TRIPOS>ATOM' && tokenizer.position < tokenizer.data.length) {
         markLine(tokenizer);
     }
 
@@ -243,7 +243,7 @@ async function handleBonds(state: State): Promise<Schema.Mol2Bonds> {
     const { tokenizer, molecule } = state;
     let hasStatus_bit = false;
 
-    while (getTokenString(tokenizer) !== '@<TRIPOS>BOND') {
+    while (getTokenString(tokenizer) !== '@<TRIPOS>BOND' && tokenizer.position < tokenizer.data.length) {
         markLine(tokenizer);
     }
 
@@ -324,7 +324,7 @@ async function handleBonds(state: State): Promise<Schema.Mol2Bonds> {
     return ret;
 }
 
-async function parseInternal(data: string, ctx: RuntimeContext): Promise<Result<Schema.Mol2File>> {
+async function parseInternal(ctx: RuntimeContext, data: string, name: string): Promise<Result<Schema.Mol2File>> {
     const tokenizer = Tokenizer(data);
 
     ctx.update({ message: 'Parsing...', current: 0, max: data.length });
@@ -335,16 +335,15 @@ async function parseInternal(data: string, ctx: RuntimeContext): Promise<Result<
         const atoms = await handleAtoms(state);
         const bonds = await handleBonds(state);
         structures.push({ molecule: state.molecule, atoms, bonds });
+        skipWhitespace(tokenizer);
     }
 
-    const result: Schema.Mol2File = { structures };
+    const result: Schema.Mol2File = { name, structures };
     return Result.success(result);
 }
 
-export function parse(data: string) {
+export function parseMol2(data: string, name: string) {
     return Task.create<Result<Schema.Mol2File>>('Parse MOL2', async ctx => {
-        return await parseInternal(data, ctx);
+        return await parseInternal(ctx, data, name);
     });
-}
-
-export default parse;
+}

+ 1 - 0
src/mol-io/reader/mol2/schema.d.ts

@@ -63,5 +63,6 @@ export interface Mol2Structure {
 }
 
 export interface Mol2File {
+    name: string
     structures: Mol2Structure[]
 }

+ 0 - 0
src/mol-model-formats/structure/format.ts → src/mol-model-formats/format.ts


+ 1 - 1
src/mol-model-formats/structure/3dg.ts

@@ -6,7 +6,7 @@
 
 import { Model } from '../../mol-model/structure/model';
 import { Task } from '../../mol-task';
-import { ModelFormat } from './format';
+import { ModelFormat } from '../format';
 import { Column, Table } from '../../mol-data/db';
 import { EntityBuilder } from './common/entity';
 import { File3DG } from '../../mol-io/reader/3dg/parser';

+ 1 - 1
src/mol-model-formats/structure/basic/atomic.ts

@@ -16,7 +16,7 @@ import { ElementSymbol } from '../../../mol-model/structure/model/types';
 import { Entities } from '../../../mol-model/structure/model/properties/common';
 import { getAtomicDerivedData } from '../../../mol-model/structure/model/properties/utils/atomic-derived';
 import { AtomSite } from './schema';
-import { ModelFormat } from '../format';
+import { ModelFormat } from '../../format';
 import { SymmetryOperator } from '../../../mol-math/geometry';
 import { MmcifFormat } from '../mmcif';
 import { AtomSiteOperatorMappingSchema } from '../../../mol-model/structure/export/categories/atom_site_operator_mapping';

+ 1 - 1
src/mol-model-formats/structure/basic/parser.ts

@@ -15,7 +15,7 @@ import { getAtomicHierarchyAndConformation } from './atomic';
 import { getCoarse, EmptyCoarse, CoarseData } from './coarse';
 import { getSequence } from './sequence';
 import { sortAtomSite } from './sort';
-import { ModelFormat } from '../format';
+import { ModelFormat } from '../../format';
 import { getAtomicRanges } from '../../../mol-model/structure/model/properties/utils/atomic-ranges';
 import { AtomSite, BasicData } from './schema';
 import { getProperties } from './properties';

+ 1 - 1
src/mol-model-formats/structure/cif-core.ts

@@ -12,7 +12,7 @@ import { createModels } from './basic/parser';
 import { BasicSchema, createBasic } from './basic/schema';
 import { ComponentBuilder } from './common/component';
 import { EntityBuilder } from './common/entity';
-import { ModelFormat } from './format';
+import { ModelFormat } from '../format';
 import { CifCore_Database } from '../../mol-io/reader/cif/schema/cif-core';
 import { CifFrame, CIF } from '../../mol-io/reader/cif';
 import { Spacegroup, SpacegroupCell } from '../../mol-math/geometry';

+ 1 - 1
src/mol-model-formats/structure/common/property.ts

@@ -5,7 +5,7 @@
  */
 
 import { CustomPropertyDescriptor, Model } from '../../../mol-model/structure';
-import { ModelFormat } from '../format';
+import { ModelFormat } from '../../format';
 
 class FormatRegistry<T> {
     private map = new Map<ModelFormat['kind'], (model: Model) => T | undefined>()

+ 1 - 1
src/mol-model-formats/structure/cube.ts

@@ -12,7 +12,7 @@ import { createModels } from './basic/parser';
 import { BasicSchema, createBasic } from './basic/schema';
 import { ComponentBuilder } from './common/component';
 import { EntityBuilder } from './common/entity';
-import { ModelFormat } from './format';
+import { ModelFormat } from '../format';
 import { CubeFile } from '../../mol-io/reader/cube/parser';
 
 async function getModels(cube: CubeFile, ctx: RuntimeContext): Promise<Model[]> {

+ 1 - 1
src/mol-model-formats/structure/gro.ts

@@ -6,7 +6,7 @@
 
 import { Model } from '../../mol-model/structure/model';
 import { Task } from '../../mol-task';
-import { ModelFormat } from './format';
+import { ModelFormat } from '../format';
 import { GroFile, GroAtoms } from '../../mol-io/reader/gro/schema';
 import { Column, Table } from '../../mol-data/db';
 import { guessElementSymbolString } from './util';

+ 1 - 1
src/mol-model-formats/structure/mmcif.ts

@@ -7,7 +7,7 @@
 
 import { Model } from '../../mol-model/structure/model/model';
 import { Task } from '../../mol-task';
-import { ModelFormat } from './format';
+import { ModelFormat } from '../format';
 import { CifFrame, CIF } from '../../mol-io/reader/cif';
 import { mmCIF_Database } from '../../mol-io/reader/cif/schema/mmcif';
 import { createModels } from './basic/parser';

+ 1 - 1
src/mol-model-formats/structure/mol.ts

@@ -14,7 +14,7 @@ import { createModels } from './basic/parser';
 import { BasicSchema, createBasic } from './basic/schema';
 import { ComponentBuilder } from './common/component';
 import { EntityBuilder } from './common/entity';
-import { ModelFormat } from './format';
+import { ModelFormat } from '../format';
 import { IndexPairBonds } from './property/bonds/index-pair';
 
 async function getModels(mol: MolFile, ctx: RuntimeContext): Promise<Model[]> {

+ 98 - 0
src/mol-model-formats/structure/mol2.ts

@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Column, Table } from '../../mol-data/db';
+import { Model } from '../../mol-model/structure/model';
+import { MoleculeType } from '../../mol-model/structure/model/types';
+import { RuntimeContext, Task } from '../../mol-task';
+import { createModels } from './basic/parser';
+import { BasicSchema, createBasic } from './basic/schema';
+import { ComponentBuilder } from './common/component';
+import { EntityBuilder } from './common/entity';
+import { ModelFormat } from '../format';
+import { IndexPairBonds } from './property/bonds/index-pair';
+import { Mol2File } from '../../mol-io/reader/mol2/schema';
+
+async function getModels(mol2: Mol2File, ctx: RuntimeContext): Promise<Model[]> {
+    const models: Model[] = [];
+
+    for (let i = 0, il = mol2.structures.length; i < il; ++i) {
+        const { atoms, bonds } = mol2.structures[i];
+
+        const A = Column.ofConst('A', atoms.count, Column.Schema.str);
+
+        const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
+            auth_asym_id: A,
+            auth_atom_id: Column.asArrayColumn(atoms.atom_type),
+            auth_comp_id: atoms.subst_name,
+            auth_seq_id: atoms.subst_id,
+            Cartn_x: Column.asArrayColumn(atoms.x, Float32Array),
+            Cartn_y: Column.asArrayColumn(atoms.y, Float32Array),
+            Cartn_z: Column.asArrayColumn(atoms.z, Float32Array),
+            id: Column.asArrayColumn(atoms.atom_id),
+
+            label_asym_id: A,
+            label_atom_id: Column.asArrayColumn(atoms.atom_type),
+            label_comp_id: atoms.subst_name,
+            label_seq_id: atoms.subst_id,
+            label_entity_id: Column.ofConst('1', atoms.count, Column.Schema.str),
+
+            occupancy: Column.ofConst(1, atoms.count, Column.Schema.float),
+            type_symbol: Column.asArrayColumn(atoms.atom_name),
+
+            pdbx_PDB_model_num: Column.ofConst(i, atoms.count, Column.Schema.int),
+        }, atoms.count);
+
+        const entityBuilder = new EntityBuilder();
+        entityBuilder.setNames([['MOL', 'Unknown Entity']]);
+        entityBuilder.getEntityId('MOL', MoleculeType.Unknown, 'A');
+
+        const componentBuilder = new ComponentBuilder(atoms.subst_id, atoms.atom_name);
+        for (let i = 0, il = atoms.subst_name.rowCount; i < il; ++i) {
+            componentBuilder.add(atoms.subst_name.value(i), i);
+        }
+
+        const basics = createBasic({
+            entity: entityBuilder.getEntityTable(),
+            chem_comp: componentBuilder.getChemCompTable(),
+            atom_site
+        });
+
+        const _models = await createModels(basics, Mol2Format.create(mol2), ctx);
+
+        if (_models.length > 0) {
+            const indexA = Column.ofIntArray(Column.mapToArray(bonds.origin_atom_id, x => x - 1, Int32Array));
+            const indexB = Column.ofIntArray(Column.mapToArray(bonds.target_atom_id, x => x - 1, Int32Array));
+            const order = Column.ofIntArray(Column.mapToArray(bonds.bond_type, x => x === 'ar' ? 1 : parseInt(x), Int8Array));
+            const pairBonds = IndexPairBonds.fromData({ pairs: { indexA, indexB, order }, count: bonds.count });
+            IndexPairBonds.Provider.set(_models[0], pairBonds);
+
+            models.push(_models[0]);
+        }
+    }
+
+    return models;
+}
+
+//
+
+export { Mol2Format };
+
+type Mol2Format = ModelFormat<Mol2File>
+
+namespace Mol2Format {
+    export function is(x: ModelFormat): x is Mol2Format {
+        return x.kind === 'mol2';
+    }
+
+    export function create(mol2: Mol2File): Mol2Format {
+        return { kind: 'mol2', name: mol2.name, data: mol2 };
+    }
+}
+
+export function trajectoryFromMol2(mol2: Mol2File): Task<Model.Trajectory> {
+    return Task.create('Parse MOL2', ctx => getModels(mol2, ctx));
+}

+ 1 - 1
src/mol-model-formats/structure/psf.ts

@@ -12,7 +12,7 @@ import { guessElementSymbolString } from './util';
 import { MoleculeType, getMoleculeType } from '../../mol-model/structure/model/types';
 import { getChainId } from './common/util';
 import { Task } from '../../mol-task';
-import { ModelFormat } from './format';
+import { ModelFormat } from '../format';
 import { Topology } from '../../mol-model/structure/topology/topology';
 import { createBasic, BasicSchema } from './basic/schema';
 

+ 19 - 1
src/mol-model-formats/volume/ccp4.ts

@@ -13,6 +13,7 @@ import { degToRad } from '../../mol-math/misc';
 import { getCcp4ValueType } from '../../mol-io/reader/ccp4/parser';
 import { TypedArrayValueType } from '../../mol-io/common/typed-array';
 import { arrayMin, arrayRms, arrayMean, arrayMax } from '../../mol-util/array';
+import { ModelFormat } from '../format';
 
 /** When available (e.g. in MRC files) use ORIGIN records instead of N[CRS]START */
 export function getCcp4Origin(header: Ccp4Header): Vec3 {
@@ -75,7 +76,24 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
                 max: isNaN(header.AMAX) ? arrayMax(values) : header.AMAX,
                 mean: isNaN(header.AMEAN) ? arrayMean(values) : header.AMEAN,
                 sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
-            }
+            },
+            sourceData: Ccp4Format.create(source)
         };
     });
+}
+
+//
+
+export { Ccp4Format };
+
+type Ccp4Format = ModelFormat<Ccp4File>
+
+namespace Ccp4Format {
+    export function is(x: ModelFormat): x is Ccp4Format {
+        return x.kind === 'ccp4';
+    }
+
+    export function create(ccp4: Ccp4File): Ccp4Format {
+        return { kind: 'ccp4', name: ccp4.name, data: ccp4 };
+    }
 }

+ 19 - 1
src/mol-model-formats/volume/cube.ts

@@ -9,6 +9,7 @@ import { Mat4, Tensor } from '../../mol-math/linear-algebra';
 import { VolumeData } from '../../mol-model/volume/data';
 import { Task } from '../../mol-task';
 import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
+import { ModelFormat } from '../format';
 
 export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number, label?: string }): Task<VolumeData> {
     return Task.create<VolumeData>('Create Volume Data', async () => {
@@ -51,7 +52,24 @@ export function volumeFromCube(source: CubeFile, params?: { dataIndex?: number,
                 max: arrayMax(values),
                 mean: arrayMean(values),
                 sigma: arrayRms(values)
-            }
+            },
+            sourceData: CubeFormat.create(source)
         };
     });
+}
+
+//
+
+export { CubeFormat };
+
+type CubeFormat = ModelFormat<CubeFile>
+
+namespace CubeFormat {
+    export function is(x: ModelFormat): x is CubeFormat {
+        return x.kind === 'cube';
+    }
+
+    export function create(cube: CubeFile): CubeFormat {
+        return { kind: 'cube', name: cube.name, data: cube };
+    }
 }

+ 19 - 3
src/mol-model-formats/volume/density-server.ts

@@ -9,8 +9,9 @@ import { VolumeData } from '../../mol-model/volume/data';
 import { Task } from '../../mol-task';
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
+import { ModelFormat } from '../format';
 
-function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
+export function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
     return Task.create<VolumeData>('Create Volume Data', async ctx => {
         const { volume_data_3d_info: info, volume_data_3d: values } = source;
         const cell = SpacegroupCell.create(
@@ -41,9 +42,24 @@ function volumeFromDensityServerData(source: DensityServer_Data_Database): Task<
                 max: info.max_sampled.value(0),
                 mean: info.mean_sampled.value(0),
                 sigma: info.sigma_sampled.value(0)
-            }
+            },
+            sourceData: DscifFormat.create(source)
         };
     });
 }
 
-export { volumeFromDensityServerData };
+//
+
+export { DscifFormat };
+
+type DscifFormat = ModelFormat<DensityServer_Data_Database>
+
+namespace DscifFormat {
+    export function is(x: ModelFormat): x is DscifFormat {
+        return x.kind === 'dscif';
+    }
+
+    export function create(dscif: DensityServer_Data_Database): DscifFormat {
+        return { kind: 'dscif', name: dscif._name, data: dscif };
+    }
+}

+ 19 - 3
src/mol-model-formats/volume/dsn6.ts

@@ -11,8 +11,9 @@ import { Tensor, Vec3 } from '../../mol-math/linear-algebra';
 import { degToRad } from '../../mol-math/misc';
 import { Dsn6File } from '../../mol-io/reader/dsn6/schema';
 import { arrayMin, arrayMax, arrayMean, arrayRms } from '../../mol-util/array';
+import { ModelFormat } from '../format';
 
-function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<VolumeData> {
+export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: string }): Task<VolumeData> {
     return Task.create<VolumeData>('Create Volume Data', async ctx => {
         const { header, values } = source;
         const size = Vec3.create(header.xlen, header.ylen, header.zlen);
@@ -40,9 +41,24 @@ function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, label?: s
                 max: arrayMax(values),
                 mean: arrayMean(values),
                 sigma: header.sigma !== undefined ? header.sigma : arrayRms(values)
-            }
+            },
+            sourceData: Dsn6Format.create(source)
         };
     });
 }
 
-export { volumeFromDsn6 };
+//
+
+export { Dsn6Format };
+
+type Dsn6Format = ModelFormat<Dsn6File>
+
+namespace Dsn6Format {
+    export function is(x: ModelFormat): x is Dsn6Format {
+        return x.kind === 'dsn6';
+    }
+
+    export function create(dsn6: Dsn6File): Dsn6Format {
+        return { kind: 'dsn6', name: dsn6.name, data: dsn6 };
+    }
+}

+ 19 - 1
src/mol-model-formats/volume/dx.ts

@@ -9,6 +9,7 @@ import { Mat4, Tensor } from '../../mol-math/linear-algebra';
 import { VolumeData } from '../../mol-model/volume/data';
 import { Task } from '../../mol-task';
 import { arrayMax, arrayMean, arrayMin, arrayRms } from '../../mol-util/array';
+import { ModelFormat } from '../format';
 
 export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<VolumeData> {
     return Task.create<VolumeData>('Create Volume Data', async () => {
@@ -28,7 +29,24 @@ export function volumeFromDx(source: DxFile, params?: { label?: string }): Task<
                 max: arrayMax(values),
                 mean: arrayMean(values),
                 sigma: arrayRms(values)
-            }
+            },
+            sourceData: DxFormat.create(source)
         };
     });
+}
+
+//
+
+export { DxFormat };
+
+type DxFormat = ModelFormat<DxFile>
+
+namespace DxFormat {
+    export function is(x: ModelFormat): x is DxFormat {
+        return x.kind === 'dx';
+    }
+
+    export function create(dx: DxFile): DxFormat {
+        return { kind: 'dx', name: dx.name, data: dx };
+    }
 }

+ 1 - 1
src/mol-model/structure/model/model.ts

@@ -12,7 +12,7 @@ import { CoarseHierarchy, CoarseConformation } from './properties/coarse';
 import { Entities, ChemicalComponentMap, MissingResidues, StructAsymMap } from './properties/common';
 import { CustomProperties } from '../common/custom-property';
 import { SaccharideComponentMap } from '../structure/carbohydrates/constants';
-import { ModelFormat } from '../../../mol-model-formats/structure/format';
+import { ModelFormat } from '../../../mol-model-formats/format';
 import { calcModelCenter } from './util';
 import { Vec3 } from '../../../mol-math/linear-algebra';
 import { Mutable } from '../../../mol-util/type-helpers';

+ 1 - 1
src/mol-model/structure/topology/topology.ts

@@ -7,7 +7,7 @@
 import { UUID } from '../../../mol-util';
 import { Column } from '../../../mol-data/db';
 import { BasicData } from '../../../mol-model-formats/structure/basic/schema';
-import { ModelFormat } from '../../../mol-model-formats/structure/format';
+import { ModelFormat } from '../../../mol-model-formats/format';
 
 export { Topology };
 

+ 4 - 1
src/mol-model/volume/data.ts

@@ -8,6 +8,7 @@
 import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
 import { Tensor, Mat4, Vec3 } from '../../mol-math/linear-algebra';
 import { equalEps } from '../../mol-math/linear-algebra/3d/common';
+import { ModelFormat } from '../../mol-model-formats/format';
 
 /** The basic unit cell that contains the data. */
 interface VolumeDataBase {
@@ -20,6 +21,7 @@ interface VolumeDataBase {
         mean: number,
         sigma: number
     }>
+    readonly sourceData: ModelFormat,
 }
 
 interface VolumeData extends VolumeDataBase {
@@ -30,7 +32,8 @@ namespace VolumeData {
     export const One: VolumeData = {
         transform: { kind: 'matrix', matrix: Mat4.identity() },
         data: Tensor.create(Tensor.Space([1, 1, 1], [0, 1, 2]), Tensor.Data1([0])),
-        dataStats: { min: 0, max: 0, mean: 0, sigma: 0 }
+        dataStats: { min: 0, max: 0, mean: 0, sigma: 0 },
+        sourceData: { kind: '', data: '', name: '' }
     };
 
     const _scale = Mat4.zero(), _translate = Mat4.zero();

+ 6 - 0
src/mol-model/volume/volume.ts

@@ -9,10 +9,16 @@ import { OrderedSet } from '../../mol-data/int';
 import { Sphere3D } from '../../mol-math/geometry';
 import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
 import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
+import { CubeFormat } from '../../mol-model-formats/volume/cube';
 
 export namespace Volume {
     export type CellIndex = { readonly '@type': 'cell-index' } & number
 
+    export function isOrbitals(volume: VolumeData) {
+        if (!CubeFormat.is(volume.sourceData)) return false;
+        return volume.sourceData.data.header.orbitals;
+    }
+
     export interface Loci { readonly kind: 'volume-loci', readonly volume: VolumeData }
     export function Loci(volume: VolumeData): Loci { return { kind: 'volume-loci', volume }; }
     export function isLoci(x: any): x is Loci { return !!x && x.kind === 'volume-loci'; }

+ 10 - 1
src/mol-plugin-state/formats/trajectory.ts

@@ -122,6 +122,14 @@ export const MolProvider: TrajectoryFormatProvider = {
     visuals: defaultVisuals
 };
 
+export const Mol2Provider: TrajectoryFormatProvider = {
+    label: 'MOL2',
+    description: 'MOL2',
+    category: Category,
+    stringExtensions: ['mol2'],
+    parse: directTrajectory(StateTransforms.Model.TrajectoryFromMOL2),
+    visuals: defaultVisuals
+};
 
 export const BuiltInTrajectoryFormats = [
     ['mmcif', MmcifProvider] as const,
@@ -129,7 +137,8 @@ export const BuiltInTrajectoryFormats = [
     ['pdb', PdbProvider] as const,
     ['gro', GroProvider] as const,
     ['3dg', Provider3dg] as const,
-    ['mol', MolProvider] as const
+    ['mol', MolProvider] as const,
+    ['mol2', Mol2Provider] as const,
 ] as const;
 
 export type BuiltInTrajectoryFormat = (typeof BuiltInTrajectoryFormats)[number][0]

+ 26 - 14
src/mol-plugin-state/formats/volume.ts

@@ -15,6 +15,7 @@ import { ColorNames } from '../../mol-util/color/names';
 import { VolumeIsoValue } from '../../mol-model/volume';
 import { createVolumeRepresentationParams } from '../helpers/volume-representation-params';
 import { objectForEach } from '../../mol-util/object';
+import { Volume } from '../../mol-model/volume/volume';
 
 const Category = 'Volume';
 
@@ -104,30 +105,41 @@ export const CubeProvider = DataFormatProvider({
     visuals: async (plugin: PluginContext, data: { volume: StateObjectSelector<PluginStateObject.Volume.Data>, structure: StateObjectSelector<PluginStateObject.Molecule.Structure> }) => {
         const surfaces = plugin.build();
 
+        const volumeReprs: StateObjectSelector<PluginStateObject.Volume.Representation3D>[] = [];
         const volumeData = data.volume.cell?.obj?.data;
-        const volumePos = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
-            type: 'isosurface',
-            typeParams: { isoValue: VolumeIsoValue.relative(1), alpha: 0.4 },
-            color: 'uniform',
-            colorParams: { value: ColorNames.blue }
-        }));
-        const volumeNeg = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
-            type: 'isosurface',
-            typeParams: { isoValue: VolumeIsoValue.relative(-1), alpha: 0.4 },
-            color: 'uniform',
-            colorParams: { value: ColorNames.red }
-        }));
+        if (volumeData && Volume.isOrbitals(volumeData)) {
+            const volumePos = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
+                type: 'isosurface',
+                typeParams: { isoValue: VolumeIsoValue.relative(1), alpha: 0.4 },
+                color: 'uniform',
+                colorParams: { value: ColorNames.blue }
+            }));
+            const volumeNeg = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
+                type: 'isosurface',
+                typeParams: { isoValue: VolumeIsoValue.relative(-1), alpha: 0.4 },
+                color: 'uniform',
+                colorParams: { value: ColorNames.red }
+            }));
+            volumeReprs.push(volumePos.selector, volumeNeg.selector);
+        } else {
+            const volume = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
+                type: 'isosurface',
+                typeParams: { isoValue: VolumeIsoValue.relative(2), alpha: 0.4 },
+                color: 'uniform',
+                colorParams: { value: ColorNames.grey }
+            }));
+            volumeReprs.push(volume.selector);
+        }
 
         const structure = await plugin.builders.structure.representation.applyPreset(data.structure, 'auto');
         await surfaces.commit();
 
         const structureReprs: StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>[] = [];
-
         objectForEach(structure?.representations as any, (r: any) => {
             if (r) structureReprs.push(r);
         });
 
-        return [volumePos.selector, volumeNeg.selector, ...structureReprs];
+        return [...volumeReprs, ...structureReprs];
     }
 });
 

+ 1 - 1
src/mol-plugin-state/manager/snapshots.ts

@@ -132,7 +132,7 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
     async setStateSnapshot(snapshot: PluginStateSnapshotManager.StateSnapshot): Promise<PluginState.Snapshot | undefined> {
         if (snapshot.version !== PLUGIN_VERSION) {
             // TODO
-            console.warn('state snapshot version mismatch');
+            // console.warn('state snapshot version mismatch');
         }
 
         this.clear();

+ 21 - 0
src/mol-plugin-state/transforms/model.ts

@@ -35,6 +35,8 @@ import { parseMol } from '../../mol-io/reader/mol/parser';
 import { trajectoryFromMol } from '../../mol-model-formats/structure/mol';
 import { trajectoryFromCifCore } from '../../mol-model-formats/structure/cif-core';
 import { trajectoryFromCube } from '../../mol-model-formats/structure/cube';
+import { parseMol2 } from '../../mol-io/reader/mol2/parser';
+import { trajectoryFromMol2 } from '../../mol-model-formats/structure/mol2';
 
 export { CoordinatesFromDcd };
 export { TopologyFromPsf };
@@ -44,6 +46,7 @@ export { TrajectoryFromMmCif };
 export { TrajectoryFromPDB };
 export { TrajectoryFromGRO };
 export { TrajectoryFromMOL };
+export { TrajectoryFromMOL2 };
 export { TrajectoryFromCube };
 export { TrajectoryFromCifCore };
 export { TrajectoryFrom3DG };
@@ -235,6 +238,24 @@ const TrajectoryFromMOL = PluginStateTransform.BuiltIn({
     }
 });
 
+type TrajectoryFromMOL2 = typeof TrajectoryFromMOL
+const TrajectoryFromMOL2 = PluginStateTransform.BuiltIn({
+    name: 'trajectory-from-mol2',
+    display: { name: 'Parse MOL2', description: 'Parse MOL2 string and create trajectory.' },
+    from: [SO.Data.String],
+    to: SO.Molecule.Trajectory
+})({
+    apply({ a }) {
+        return Task.create('Parse MOL2', async ctx => {
+            const parsed = await parseMol2(a.data, a.label).runInContext(ctx);
+            if (parsed.isError) throw new Error(parsed.message);
+            const models = await trajectoryFromMol2(parsed.result).runInContext(ctx);
+            const props = { label: `${models[0].entry}`, description: `${models.length} model${models.length === 1 ? '' : 's'}` };
+            return new SO.Molecule.Trajectory(models, props);
+        });
+    }
+});
+
 type TrajectoryFromCube = typeof TrajectoryFromCube
 const TrajectoryFromCube = PluginStateTransform.BuiltIn({
     name: 'trajectory-from-cube',

Plik diff jest za duży
+ 1 - 45
src/mol-plugin-ui/skin/base/logo.scss


+ 1 - 8
src/mol-plugin-ui/viewport.tsx

@@ -130,14 +130,7 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
 }
 
 export const Logo = () =>
-    <div className='msp-logo'>
-        <div>
-            <div>
-                <div />
-                <div className='msp-logo-image' />
-            </div>
-        </div>
-    </div>;
+    <a className='msp-logo' href='https://molstar.org' target='_blank' />;
 
 interface ViewportState {
     noWebGl: boolean

+ 7 - 4
src/mol-plugin/context.ts

@@ -58,6 +58,7 @@ import { PluginStateSnapshotManager } from '../mol-plugin-state/manager/snapshot
 import { PluginAnimationManager } from '../mol-plugin-state/manager/animation';
 import { objectForEach } from '../mol-util/object';
 import { VolumeHierarchyManager } from '../mol-plugin-state/manager/volume/hierarchy';
+import { filter, take } from 'rxjs/operators';
 
 export class PluginContext {
     runTask = <T>(task: Task<T>) => this.tasks.run(task);
@@ -73,13 +74,13 @@ export class PluginContext {
         log: this.ev<LogEntry>(),
         task: this.tasks.events,
         canvas3d: {
-            initialized: this.ev(),
             settingsUpdated: this.ev(),
         }
     } as const
 
     readonly config = new PluginConfigManager(this.spec.config);
 
+    private canvas3dInit = this.ev.behavior<boolean>(false);
     readonly behaviors = {
         state: {
             isAnimating: this.ev.behavior<boolean>(false),
@@ -96,8 +97,11 @@ export class PluginContext {
         },
         layout: {
             leftPanelTabName: this.ev.behavior<LeftPanelTabName>('root')
+        },
+        canvas3d: {
+            initialized: this.canvas3dInit.pipe(filter(v => !!v), take(1))
         }
-    } as const
+    } as const;
 
     readonly canvas3d: Canvas3D | undefined;
     readonly layout = new PluginLayout(this);
@@ -174,8 +178,7 @@ export class PluginContext {
             if (this.spec.layout && this.spec.layout.initial) this.layout.setProps(this.spec.layout.initial);
 
             (this.canvas3d as Canvas3D) = Canvas3D.fromCanvas(canvas);
-            this.events.canvas3d.initialized.next();
-            this.events.canvas3d.initialized.isStopped = true; // TODO is this a good way?
+            this.canvas3dInit.next(true);
             const renderer = this.canvas3d!.props.renderer;
             PluginCommands.Canvas3D.SetSettings(this, { settings: { renderer: { ...renderer, backgroundColor: Color(0xFCFBF9) } } });
             this.canvas3d!.animate();

+ 1 - 1
src/mol-repr/structure/visual/label-text.ts

@@ -148,7 +148,7 @@ function createElementText(ctx: VisualContext, structure: Structure, theme: Them
     const { label_atom_id, label_alt_id } = StructureProperties.atom;
     const { cumulativeUnitElementCount } = serialMapping;
 
-    const sizeTheme = PhysicalSizeTheme({}, {});
+    const sizeTheme = PhysicalSizeTheme({}, { scale: 1 });
 
     const count = structure.elementCount;
     const { elementScale } = props;

+ 2 - 2
src/mol-repr/structure/visual/util/common.ts

@@ -175,7 +175,7 @@ export function getUnitConformationAndRadius(structure: Structure, unit: Unit, p
     const boundary = unit === rootUnit ? unit.boundary : getBoundary(position);
 
     const l = StructureElement.Location.create(structure, rootUnit);
-    const sizeTheme = PhysicalSizeTheme({}, {});
+    const sizeTheme = PhysicalSizeTheme({}, { scale: 1 });
     const radius = (index: number) => {
         l.element = index as ElementIndex;
         return sizeTheme.size(l);
@@ -186,7 +186,7 @@ export function getUnitConformationAndRadius(structure: Structure, unit: Unit, p
 
 export function getStructureConformationAndRadius(structure: Structure, ignoreHydrogens: boolean, traceOnly: boolean) {
     const l = StructureElement.Location.create(structure);
-    const sizeTheme = PhysicalSizeTheme({}, {});
+    const sizeTheme = PhysicalSizeTheme({}, { scale: 1 });
 
     let xs: ArrayLike<number>;
     let ys: ArrayLike<number>;

+ 8 - 4
src/mol-theme/size/physical.ts

@@ -14,7 +14,9 @@ import { ThemeDataContext } from '../../mol-theme/theme';
 const DefaultSize = 1;
 const Description = 'Assigns a physical size, i.e. vdW radius for atoms or given radius for coarse spheres.';
 
-export const PhysicalSizeThemeParams = {};
+export const PhysicalSizeThemeParams = {
+    scale: PD.Numeric(1, { min: 0.1, max: 5, step: 0.1 })
+};
 export type PhysicalSizeThemeParams = typeof PhysicalSizeThemeParams
 export function getPhysicalSizeThemeParams(ctx: ThemeDataContext) {
     return PhysicalSizeThemeParams; // TODO return copy
@@ -35,14 +37,16 @@ export function getPhysicalRadius(unit: Unit, element: ElementIndex): number {
  * i.e. vdw for atoms and radius for coarse spheres
  */
 export function PhysicalSizeTheme(ctx: ThemeDataContext, props: PD.Values<PhysicalSizeThemeParams>): SizeTheme<PhysicalSizeThemeParams> {
+    const scale = props.scale === void 0 ? 1 : props.scale;
+
     function size(location: Location): number {
         let size: number;
         if (StructureElement.Location.is(location)) {
-            size = getPhysicalRadius(location.unit, location.element);
+            size = scale * getPhysicalRadius(location.unit, location.element);
         } else if (Bond.isLocation(location)) {
-            size = getPhysicalRadius(location.aUnit, location.aUnit.elements[location.aIndex]);
+            size = scale * getPhysicalRadius(location.aUnit, location.aUnit.elements[location.aIndex]);
         } else {
-            size = DefaultSize;
+            size = scale * DefaultSize;
         }
         return size;
     }

+ 1 - 2
src/mol-util/data-source.ts

@@ -241,9 +241,8 @@ function processAjax<T extends DataType>(req: XMLHttpRequest, type: T): DataResp
         }
         throw new Error(`could not get requested response data '${type}'`);
     } else {
-        const status = req.statusText;
         RequestPool.deposit(req);
-        throw new Error(status);
+        throw new Error(`Download failed with status code ${req.status}`);
     }
 }
 

+ 9 - 4
src/mol-util/param-definition.ts

@@ -271,7 +271,7 @@ export namespace ParamDefinition {
         getLabel(t: T): string
     }
     export function ObjectList<T>(element: For<T>, getLabel: (e: T) => string, info?: Info & { defaultValue?: T[], ctor?: () => T }): ObjectList<Normalize<T>> {
-        return setInfo<ObjectList<Normalize<T>>>({ type: 'object-list', element: element as any as Params, getLabel, ctor: _defaultObjectListCtor, defaultValue: (info?.defaultValue) || []  }, info);
+        return setInfo<ObjectList<Normalize<T>>>({ type: 'object-list', element: element as any as Params, getLabel, ctor: _defaultObjectListCtor, defaultValue: (info?.defaultValue) || [] }, info);
     }
     function _defaultObjectListCtor(this: ObjectList) { return getDefaultValues(this.element) as any; }
 
@@ -316,7 +316,7 @@ export namespace ParamDefinition {
     export type ValuesFor<T extends For<any>> = Normalize<{ [k in keyof T]: T[k]['defaultValue'] }>
 
     type Optionals<P> = { [K in keyof P]-?: undefined extends P[K] ? K : never }[keyof P]
-    type NonOptionals<P> = { [K in keyof P]-?: undefined extends P[K] ? never: K }[keyof P]
+    type NonOptionals<P> = { [K in keyof P]-?: undefined extends P[K] ? never : K }[keyof P]
     export type Normalize<P> = Pick<P, NonOptionals<P>> & Partial<Pick<P, Optionals<P>>>
     export type For<P> = { [K in keyof P]-?: Base<P[K]> }
     export type Def<P> = { [K in keyof P]: Any }
@@ -435,8 +435,8 @@ export namespace ParamDefinition {
     }
 
     export function mergeParam(p: Any, a: any, b: any): any {
-        if (a === undefined) return typeof b === 'object' ? { ...b } : b;
-        if (b === undefined) return typeof a === 'object' ? { ...a } : a;
+        if (a === undefined) return typeof b === 'object' && !Array.isArray(b) ? { ...b } : b;
+        if (b === undefined) return typeof a === 'object' && !Array.isArray(a) ? { ...a } : a;
 
         if (p.type === 'group') {
             return merge(p.params, a, b);
@@ -448,7 +448,12 @@ export namespace ParamDefinition {
                 name: v.name,
                 params: mergeParam(map, u.params, v.params)
             };
+        } else if (p.type === 'value') {
+            return b;
         } else if (typeof a === 'object' && typeof b === 'object') {
+            if (Array.isArray(b)) {
+                return b;
+            }
             return { ...a, ...b };
         } else {
             return b;

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików