diff --git a/package-lock.json b/package-lock.json index 234311741bcb83cce482b6b4833806dafdeddea4..25839c99f0e4801877e5323717bc726ccf6d8e19 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index fe60146857eda7801a1381f1c119551b13256d7e..f9391796ea11f2467d2a04af2daeb5ef05f24054 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "molstar", - "version": "1.1.3", + "version": "1.1.5", "description": "A comprehensive macromolecular library.", "homepage": "https://github.com/molstar/molstar#readme", "repository": { @@ -86,48 +86,48 @@ ], "license": "MIT", "devDependencies": { - "@graphql-codegen/add": "^1.14.0", - "@graphql-codegen/cli": "^1.14.0", - "@graphql-codegen/time": "^1.14.0", - "@graphql-codegen/typescript": "^1.14.0", - "@graphql-codegen/typescript-graphql-files-modules": "^1.14.0", - "@graphql-codegen/typescript-graphql-request": "^1.14.0", - "@graphql-codegen/typescript-operations": "^1.14.0", + "@graphql-codegen/add": "^1.17.3", + "@graphql-codegen/cli": "^1.17.3", + "@graphql-codegen/time": "^1.17.3", + "@graphql-codegen/typescript": "^1.17.3", + "@graphql-codegen/typescript-graphql-files-modules": "^1.17.3", + "@graphql-codegen/typescript-graphql-request": "^1.17.3", + "@graphql-codegen/typescript-operations": "^1.17.3", "@types/cors": "^2.8.6", - "@typescript-eslint/eslint-plugin": "^3.0.0", - "@typescript-eslint/parser": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^3.7.0", + "@typescript-eslint/parser": "^3.7.0", "benchmark": "^2.1.4", "concurrently": "^5.2.0", "cpx2": "^2.0.0", - "css-loader": "^3.5.3", - "eslint": "^7.0.0", + "css-loader": "^3.6.0", + "eslint": "^7.5.0", "extra-watch-webpack-plugin": "^1.0.3", "file-loader": "^6.0.0", - "fs-extra": "^9.0.0", - "graphql": "^15.0.0", + "fs-extra": "^9.0.1", + "graphql": "^15.3.0", "http-server": "^0.12.3", - "jest": "^26.0.1", + "jest": "^26.1.0", "mini-css-extract-plugin": "^0.9.0", "node-sass": "^4.14.1", "raw-loader": "^4.0.1", "sass-loader": "^8.0.2", - "simple-git": "^2.5.0", + "simple-git": "^2.14.0", "style-loader": "^1.2.1", - "ts-jest": "^26.0.0", - "typescript": "^3.9.3", + "ts-jest": "^26.1.3", + "typescript": "^3.9.7", "webpack": "^4.43.0", - "webpack-cli": "^3.3.11", + "webpack-cli": "^3.3.12", "webpack-version-file-plugin": "^0.4.0" }, "dependencies": { "@types/argparse": "^1.0.38", "@types/benchmark": "^1.0.33", "@types/compression": "1.7.0", - "@types/express": "^4.17.6", + "@types/express": "^4.17.7", "@types/jest": "^25.2.3", - "@types/node": "^14.0.5", + "@types/node": "^14.0.24", "@types/node-fetch": "^2.5.7", - "@types/react": "^16.9.35", + "@types/react": "^16.9.43", "@types/react-dom": "^16.9.8", "@types/swagger-ui-dist": "3.0.5", "argparse": "^1.0.10", @@ -135,13 +135,13 @@ "compression": "^1.7.4", "cors": "^2.8.5", "express": "^4.17.1", - "immer": "^6.0.6", + "immer": "^7.0.5", "immutable": "^3.8.2", "node-fetch": "^2.6.0", "react": "^16.13.1", "react-dom": "^16.13.1", - "rxjs": "^6.5.5", - "swagger-ui-dist": "^3.25.4", + "rxjs": "^6.6.0", + "swagger-ui-dist": "^3.30.1", "tslib": "^2.0.0", "util.promisify": "^1.0.1", "xhr2": "^0.2.0" diff --git a/src/examples/docking-viewer/index.html b/src/apps/docking-viewer/index.html similarity index 65% rename from src/examples/docking-viewer/index.html rename to src/apps/docking-viewer/index.html index 24e6504181db342b753449065b662b0377c1f575..2c0d1567925400dbef3bb702de3e39fae292e784 100644 --- a/src/examples/docking-viewer/index.html +++ b/src/apps/docking-viewer/index.html @@ -17,22 +17,9 @@ </head> <body> <div id="app"></div> - <script type="text/javascript" src="./index.js"></script> + <script type="text/javascript" src="./molstar.js"></script> <script type="text/javascript"> - var viewer = new DockingViewer('app', { - layoutIsExpanded: false, - layoutShowControls: false, - layoutShowRemoteState: false, - layoutShowSequence: true, - layoutShowLog: false, - layoutShowLeftPanel: true, - - viewportShowExpand: true, - viewportShowControls: false, - viewportShowSettings: false, - viewportShowSelectionMode: false, - viewportShowAnimation: false, - }); + var viewer = new DockingViewer('app', [0x33DD22, 0x1133EE], true); function getParam(name, regex) { var r = new RegExp(name + '=' + '(' + regex + ')[&]?', 'i'); diff --git a/src/examples/docking-viewer/index.ts b/src/apps/docking-viewer/index.ts similarity index 64% rename from src/examples/docking-viewer/index.ts rename to src/apps/docking-viewer/index.ts index 96f25a55215da3067318c0ef9d4eb4a61ff43561..c288b158acc3927f496cb4a6fc8011857f83b519 100644 --- a/src/examples/docking-viewer/index.ts +++ b/src/apps/docking-viewer/index.ts @@ -11,12 +11,8 @@ import './index.html'; import { PluginContext } from '../../mol-plugin/context'; import { PluginCommands } from '../../mol-plugin/commands'; import { PluginSpec } from '../../mol-plugin/spec'; -import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure'; import { PluginConfig } from '../../mol-plugin/config'; -import { Asset } from '../../mol-util/assets'; import { ObjectKeys } from '../../mol-util/type-helpers'; -import { PluginState } from '../../mol-plugin/state'; -import { DownloadDensity } from '../../mol-plugin-state/actions/volume'; import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout'; import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory'; import { Structure } from '../../mol-model/structure'; @@ -24,9 +20,10 @@ import { PluginStateTransform, PluginStateObject as PSO } from '../../mol-plugin import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { Task } from '../../mol-task'; import { StateObject } from '../../mol-state'; -import { ViewportComponent, StructurePreset } from './viewport'; +import { ViewportComponent, StructurePreset, ShowButtons } from './viewport'; import { PluginBehaviors } from '../../mol-plugin/behavior'; import { ColorNames } from '../../mol-util/color/names'; +import { Color } from '../../mol-util/color'; require('mol-plugin-ui/skin/light.scss'); @@ -53,13 +50,25 @@ const DefaultViewerOptions = { pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue, emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue, }; -type ViewerOptions = typeof DefaultViewerOptions; class Viewer { plugin: PluginContext - constructor(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) { - const o = { ...DefaultViewerOptions, ...options }; + constructor(elementOrId: string | HTMLElement, colors = [Color(0x992211), Color(0xDDDDDD)], showButtons = true) { + const o = { ...DefaultViewerOptions, ...{ + layoutIsExpanded: false, + layoutShowControls: false, + layoutShowRemoteState: false, + layoutShowSequence: true, + layoutShowLog: false, + layoutShowLeftPanel: true, + + viewportShowExpand: true, + viewportShowControls: false, + viewportShowSettings: false, + viewportShowSelectionMode: false, + viewportShowAnimation: false, + } }; const spec: PluginSpec = { actions: [...DefaultPluginSpec.actions], @@ -104,7 +113,8 @@ class Viewer { [PluginConfig.State.CurrentServer, o.pluginStateServer], [PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer], [PluginConfig.Download.DefaultPdbProvider, o.pdbProvider], - [PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider] + [PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider], + [ShowButtons, showButtons] ] }; @@ -114,40 +124,27 @@ class Viewer { if (!element) throw new Error(`Could not get element with id '${elementOrId}'`); this.plugin = createPlugin(element, spec); - PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { - renderer: { - ...this.plugin.canvas3d!.props.renderer, - backgroundColor: ColorNames.white, - }, - camera: { - ...this.plugin.canvas3d!.props.camera, - helper: { axes: { name: 'off', params: {} } } + (this.plugin.customState as any) = { + colorPalette: { + name: 'colors', + params: { list: { colors } } } - } }); - } - - setRemoteSnapshot(id: string) { - const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`; - return PluginCommands.State.Snapshots.Fetch(this.plugin, { url }); - } - - loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) { - return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type }); - } + }; - loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false) { - const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin); - return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, { - source: { - name: 'url', - params: { - url: Asset.Url(url), - format: format as any, - isBinary, - options: params.source.params.options, - } + this.plugin.behaviors.canvas3d.initialized.subscribe(v => { + if (v) { + PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { + renderer: { + ...this.plugin.canvas3d!.props.renderer, + backgroundColor: ColorNames.white, + }, + camera: { + ...this.plugin.canvas3d!.props.camera, + helper: { axes: { name: 'off', params: {} } } + } + } }); } - })); + }); } async loadStructuresFromUrlsAndMerge(sources: { url: string, format: BuiltInTrajectoryFormat, isBinary?: boolean }[]) { @@ -162,69 +159,19 @@ class Viewer { structures.push({ ref: structureProperties?.ref || structure.ref }); } + + // remove current structuresfrom hierarchy as they will be merged + // TODO only works with using loadStructuresFromUrlsAndMerge once + // need some more API metho to work with the hierarchy + this.plugin.managers.structure.hierarchy.updateCurrent(this.plugin.managers.structure.hierarchy.current.structures, 'remove'); + const dependsOn = structures.map(({ ref }) => ref); const data = this.plugin.state.data.build().toRoot().apply(MergeStructures, { structures }, { dependsOn }); const structure = await data.commit(); const structureProperties = await this.plugin.builders.structure.insertStructureProperties(structure); - await this.plugin.builders.structure.representation.applyPreset(structureProperties || structure, StructurePreset); - } - - async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) { - const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel }); - const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format); - await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default'); - } - - loadPdb(pdb: string) { - const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin); - const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!; - return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, { - source: { - name: 'pdb' as const, - params: { - provider: { - id: pdb, - server: { - name: provider, - params: PdbDownloadProvider[provider].defaultValue as any - } - }, - options: params.source.params.options, - } - } - })); - } - - loadPdbDev(pdbDev: string) { - const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin); - return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, { - source: { - name: 'pdb-dev' as const, - params: { - provider: { - id: pdbDev, - encoding: 'bcif', - }, - options: params.source.params.options, - } - } - })); - } - - loadEmdb(emdb: string) { - const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!; - return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, { - source: { - name: 'pdb-emd-ds' as const, - params: { - provider: { - id: emdb, - server: provider, - }, - detail: 3, - } - } - })); + this.plugin.behaviors.canvas3d.initialized.subscribe(async v => { + await this.plugin.builders.structure.representation.applyPreset(structureProperties || structure, StructurePreset); + }); } } @@ -260,4 +207,5 @@ const MergeStructures = PluginStateTransform.BuiltIn({ } }); -(window as any).DockingViewer = Viewer; \ No newline at end of file +(window as any).DockingViewer = Viewer; +export { Viewer as DockingViewer }; \ No newline at end of file diff --git a/src/examples/docking-viewer/viewport.tsx b/src/apps/docking-viewer/viewport.tsx similarity index 63% rename from src/examples/docking-viewer/viewport.tsx rename to src/apps/docking-viewer/viewport.tsx index 8e11e6b5ef168bc2efeac6615ac7effdc461b909..ed41fd7542c9cfae40414619c591060151ad052b 100644 --- a/src/examples/docking-viewer/viewport.tsx +++ b/src/apps/docking-viewer/viewport.tsx @@ -17,10 +17,11 @@ import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-pl import { MolScriptBuilder as MS } from '../../mol-script/language/builder'; import { InteractionsRepresentationProvider } from '../../mol-model-props/computed/representations/interactions'; import { InteractionTypeColorThemeProvider } from '../../mol-model-props/computed/themes/interaction-type'; -import { compile } from '../../mol-script/runtime/query/compiler'; -import { StructureSelection, QueryContext, Structure } from '../../mol-model/structure'; import { PluginCommands } from '../../mol-plugin/commands'; import { PluginContext } from '../../mol-plugin/context'; +import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state'; +import { Color } from '../../mol-util/color'; +import { PluginConfig } from '../../mol-plugin/config'; function shinyStyle(plugin: PluginContext) { return PluginCommands.Canvas3D.SetSettings(plugin, { settings: { @@ -76,6 +77,8 @@ const PresetParams = { ...StructureRepresentationPresetProvider.CommonParams, }; + + export const StructurePreset = StructureRepresentationPresetProvider({ id: 'preset-structure', display: { name: 'Structure' }, @@ -89,10 +92,10 @@ export const StructurePreset = StructureRepresentationPresetProvider({ polymer: await presetStaticComponent(plugin, structureCell, 'polymer'), }; - const { update, builder, typeParams, color } = StructureRepresentationPresetProvider.reprBuilder(plugin, params); + const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params); const representations = { - ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color }, { tag: 'ligand' }), - polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams }, color }, { tag: 'polymer' }), + ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.35 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }), + polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams }, color: 'chain-id', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }), }; await update.commit({ revertOnError: true }); @@ -112,12 +115,14 @@ export const IllustrativePreset = StructureRepresentationPresetProvider({ if (!structureCell) return {}; const components = { - all: await presetStaticComponent(plugin, structureCell, 'all') + ligand: await presetStaticComponent(plugin, structureCell, 'ligand'), + polymer: await presetStaticComponent(plugin, structureCell, 'polymer'), }; const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params); const representations = { - all: builder.buildRepresentation(update, components.all, { type: 'spacefill', typeParams: { ...typeParams }, color: 'illustrative' }, { tag: 'all' }), + ligand: builder.buildRepresentation(update, components.ligand, { type: 'spacefill', typeParams: { ...typeParams }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }), + polymer: builder.buildRepresentation(update, components.polymer, { type: 'spacefill', typeParams: { ...typeParams }, color: 'illustrative', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }), }; await update.commit({ revertOnError: true }); @@ -128,6 +133,34 @@ export const IllustrativePreset = StructureRepresentationPresetProvider({ } }); +const SurfacePreset = StructureRepresentationPresetProvider({ + id: 'preset-surface', + display: { name: 'Surface' }, + params: () => PresetParams, + async apply(ref, params, plugin) { + const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref); + const structure = structureCell?.obj?.data; + if (!structureCell || !structure) return {}; + + const components = { + ligand: await presetStaticComponent(plugin, structureCell, 'ligand'), + polymer: await presetStaticComponent(plugin, structureCell, 'polymer'), + }; + + const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params); + const representations = { + ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }), + polymer: builder.buildRepresentation(update, components.polymer, { type: 'molecular-surface', typeParams: { ...typeParams, quality: 'custom', resolution: 0.5, doubleSided: true }, color: 'partial-charge' }, { tag: 'polymer' }), + }; + + await update.commit({ revertOnError: true }); + await shinyStyle(plugin); + plugin.managers.interactivity.setProps({ granularity: 'residue' }); + + return { components, representations }; + } +}); + const PocketPreset = StructureRepresentationPresetProvider({ id: 'preset-pocket', display: { name: 'Pocket' }, @@ -144,7 +177,7 @@ const PocketPreset = StructureRepresentationPresetProvider({ const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params); const representations = { - ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'partial-charge' }, { tag: 'ligand' }), + ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }), surroundings: builder.buildRepresentation(update, components.surroundings, { type: 'molecular-surface', typeParams: { ...typeParams, includeParent: true, quality: 'custom', resolution: 0.2, doubleSided: true }, color: 'partial-charge' }, { tag: 'surroundings' }), }; @@ -152,11 +185,6 @@ const PocketPreset = StructureRepresentationPresetProvider({ await shinyStyle(plugin); plugin.managers.interactivity.setProps({ granularity: 'element' }); - const compiled = compile<StructureSelection>(StructureSelectionQueries.ligand.expression); - const result = compiled(new QueryContext(structure)); - const selection = StructureSelection.unionStructure(result); - plugin.managers.camera.focusLoci(Structure.toStructureElementLoci(selection)); - return { components, representations }; } }); @@ -172,56 +200,46 @@ const InteractionsPreset = StructureRepresentationPresetProvider({ const components = { ligand: await presetStaticComponent(plugin, structureCell, 'ligand'), - selection: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandPlusSurroundings, `selection`) + surroundings: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandSurroundings, `surroundings`), + interactions: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandPlusSurroundings, `interactions`) }; const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params); const representations = { - ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.26 }, color: 'partial-charge' }, { tag: 'ligand' }), - ballAndStick: builder.buildRepresentation(update, components.selection, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'partial-charge' }, { tag: 'ball-and-stick' }), - interactions: builder.buildRepresentation(update, components.selection, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }), + ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }), + ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }), + interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }), + label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }), }; await update.commit({ revertOnError: true }); await shinyStyle(plugin); plugin.managers.interactivity.setProps({ granularity: 'element' }); - const compiled = compile<StructureSelection>(StructureSelectionQueries.ligand.expression); - const result = compiled(new QueryContext(structure)); - const selection = StructureSelection.unionStructure(result); - plugin.managers.camera.focusLoci(Structure.toStructureElementLoci(selection)); - return { components, representations }; } }); +export const ShowButtons = PluginConfig.item('showButtons', true); + export class ViewportComponent extends PluginUIComponent { - structurePreset = () => { - this.plugin.managers.structure.component.applyPreset( - this.plugin.managers.structure.hierarchy.selection.structures, - StructurePreset - ); + async _set(structures: readonly StructureRef[], preset: StructureRepresentationPresetProvider) { + await this.plugin.managers.structure.component.clear(structures); + await this.plugin.managers.structure.component.applyPreset(structures, preset); } - illustrativePreset = () => { - this.plugin.managers.structure.component.applyPreset( - this.plugin.managers.structure.hierarchy.selection.structures, - IllustrativePreset - ); + set = async (preset: StructureRepresentationPresetProvider) => { + await this._set(this.plugin.managers.structure.hierarchy.selection.structures, preset); } - pocketPreset = () => { - this.plugin.managers.structure.component.applyPreset( - this.plugin.managers.structure.hierarchy.selection.structures, - PocketPreset - ); - } + structurePreset = () => this.set(StructurePreset); + illustrativePreset = () => this.set(IllustrativePreset); + surfacePreset = () => this.set(SurfacePreset); + pocketPreset = () => this.set(PocketPreset); + interactionsPreset = () => this.set(InteractionsPreset); - interactionsPreset = () => { - this.plugin.managers.structure.component.applyPreset( - this.plugin.managers.structure.hierarchy.selection.structures, - InteractionsPreset - ); + get showButtons () { + return this.plugin.config.get(ShowButtons); } render() { @@ -229,7 +247,7 @@ export class ViewportComponent extends PluginUIComponent { return <> <Viewport /> - <div className='msp-viewport-top-left-controls'> + {this.showButtons && <div className='msp-viewport-top-left-controls'> <div style={{ marginBottom: '4px' }}> <Button onClick={this.structurePreset} >Structure</Button> </div> @@ -237,12 +255,15 @@ export class ViewportComponent extends PluginUIComponent { <Button onClick={this.illustrativePreset}>Illustrative</Button> </div> <div style={{ marginBottom: '4px' }}> - <Button onClick={this.pocketPreset}>Pocket</Button> + <Button onClick={this.surfacePreset}>Surface</Button> </div> + {/* <div style={{ marginBottom: '4px' }}> + <Button onClick={this.pocketPreset}>Pocket</Button> + </div> */} <div style={{ marginBottom: '4px' }}> <Button onClick={this.interactionsPreset}>Interactions</Button> </div> - </div> + </div>} <VPControls /> <BackgroundTaskProgress /> <div className='msp-highlight-toast-wrapper'> diff --git a/src/apps/viewer/index.ts b/src/apps/viewer/index.ts index f64698cf11d0e1f44a0ecb55780df8f7e81bf891..a476588db89e170020d270d79c1649c97c65a2e4 100644 --- a/src/apps/viewer/index.ts +++ b/src/apps/viewer/index.ts @@ -92,7 +92,7 @@ export class Viewer { }, components: { ...DefaultPluginSpec.components, - remoteState: o.layoutShowRemoteState ? 'default' : 'none', + remoteState: o.layoutShowRemoteState ? 'default' : 'none' }, config: [ [PluginConfig.Viewport.ShowExpand, o.viewportShowExpand], diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 8cb502e3616404709ba9b16fc93a1805026de819..3c4b587af398b0c6a1136b53e40f635b7ee01958 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -66,6 +66,7 @@ export const Canvas3DParams = { }; export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams); export type Canvas3DProps = PD.Values<typeof Canvas3DParams> +export type PartialCanvas3DProps = { [K in keyof Canvas3DProps]?: Partial<Canvas3DProps[K]> } export { Canvas3D }; @@ -97,7 +98,7 @@ interface Canvas3D { readonly camera: Camera readonly boundingSphere: Readonly<Sphere3D> getPixelData(variant: GraphicsRenderVariant): PixelData - setProps(props: Partial<Canvas3DProps> | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void)): void + setProps(props: PartialCanvas3DProps | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void)): void getImagePass(props: Partial<ImageProps>): ImagePass /** Returns a copy of the current Canvas3D instance props */ @@ -530,7 +531,7 @@ namespace Canvas3D { didDraw, reprCount, setProps: (properties) => { - const props: Partial<Canvas3DProps> = typeof properties === 'function' + const props: PartialCanvas3DProps = typeof properties === 'function' ? produce(getProps(), properties) : properties; @@ -538,7 +539,7 @@ namespace Canvas3D { if (props.camera && props.camera.mode !== undefined && props.camera.mode !== camera.state.mode) { cameraState.mode = props.camera.mode; } - if (props.cameraFog !== undefined) { + if (props.cameraFog !== undefined && props.cameraFog.params) { const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0; if (newFog !== camera.state.fog) cameraState.fog = newFog; } diff --git a/src/mol-io/reader/cif/data-model.ts b/src/mol-io/reader/cif/data-model.ts index 3cc29133e9442ec5de6fd1d4ca94704bd80c13ac..f9a4f4213a1d42cb38ecbb6371d89c2d1f97cdfa 100644 --- a/src/mol-io/reader/cif/data-model.ts +++ b/src/mol-io/reader/cif/data-model.ts @@ -265,6 +265,10 @@ export namespace CifField { toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params) }; } + + export function ofUndefined(rowCount: number, schema: Column.Schema): CifField { + return ofColumn(Column.Undefined(rowCount, schema)); + } } export function tensorFieldNameGetter(field: string, rank: number, zeroIndexed: boolean, namingVariant: 'brackets' | 'underscore') { diff --git a/src/mol-model-formats/structure/basic/atomic.ts b/src/mol-model-formats/structure/basic/atomic.ts index 4cfb2634ddce122882d06060121fac44b195092f..d2fbfa69f055a6170862ea2ca88a82556e280556 100644 --- a/src/mol-model-formats/structure/basic/atomic.ts +++ b/src/mol-model-formats/structure/basic/atomic.ts @@ -67,13 +67,27 @@ function createHierarchyData(atom_site: AtomSite, sourceIndex: Column<number>, o }); const residues = Table.view(atom_site, ResiduesSchema, offsets.residues); + const chains = Table.view(atom_site, ChainsSchema, offsets.chains); + + if (!residues.label_seq_id.isDefined) { + const seqIds = new Int32Array(residues.label_seq_id.rowCount); + const { residues: residueOffsets, chains: chainOffsets } = offsets; + let cI = 0; + let seqId = 0; + for (let i = 0, il = seqIds.length; i < il; ++i) { + if (chainOffsets[cI] > residueOffsets[i]) { + cI += 1; + seqId = 0; + } + seqIds[i] = ++seqId; + } + residues.label_seq_id = Column.ofIntArray(seqIds); + } // Optimize the numeric columns Table.columnToArray(residues, 'label_seq_id', Int32Array); Table.columnToArray(residues, 'auth_seq_id', Int32Array); - const chains = Table.view(atom_site, ChainsSchema, offsets.chains); - // Fix possibly missing auth_/label_ columns substUndefinedColumn(atoms, 'label_atom_id', 'auth_atom_id'); substUndefinedColumn(atoms, 'label_comp_id', 'auth_comp_id'); diff --git a/src/mol-model-formats/structure/pdb/atom-site.ts b/src/mol-model-formats/structure/pdb/atom-site.ts index 3ffacc79b74a608669cb96960847f9d94181159f..933499f755aa3e6cc2697996d348a371a90af13e 100644 --- a/src/mol-model-formats/structure/pdb/atom-site.ts +++ b/src/mol-model-formats/structure/pdb/atom-site.ts @@ -9,6 +9,7 @@ import { CifField } from '../../../mol-io/reader/cif'; import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif'; import { TokenBuilder, Tokenizer } from '../../../mol-io/reader/common/text/tokenizer'; import { guessElementSymbolTokens } from '../util'; +import { Column } from '../../../mol-data/db'; type AtomSiteTemplate = typeof getAtomSiteTemplate extends (...args: any) => infer T ? T : never export function getAtomSiteTemplate(data: string, count: number) { @@ -42,13 +43,12 @@ export function getAtomSite(sites: AtomSiteTemplate): { [K in keyof mmCIF_Schema const auth_asym_id = CifField.ofTokens(sites.auth_asym_id); const auth_atom_id = CifField.ofTokens(sites.auth_atom_id); const auth_comp_id = CifField.ofTokens(sites.auth_comp_id); - const auth_seq_id = CifField.ofTokens(sites.auth_seq_id); return { auth_asym_id, auth_atom_id, auth_comp_id, - auth_seq_id, + auth_seq_id: CifField.ofTokens(sites.auth_seq_id), B_iso_or_equiv: CifField.ofTokens(sites.B_iso_or_equiv), Cartn_x: CifField.ofTokens(sites.Cartn_x), Cartn_y: CifField.ofTokens(sites.Cartn_y), @@ -61,7 +61,7 @@ export function getAtomSite(sites: AtomSiteTemplate): { [K in keyof mmCIF_Schema label_asym_id: auth_asym_id, label_atom_id: auth_atom_id, label_comp_id: auth_comp_id, - label_seq_id: auth_seq_id, + label_seq_id: CifField.ofUndefined(sites.count, Column.Schema.int), label_entity_id: CifField.ofStrings(sites.label_entity_id), occupancy: CifField.ofTokens(sites.occupancy), diff --git a/src/mol-model-formats/structure/pdb/secondary-structure.ts b/src/mol-model-formats/structure/pdb/secondary-structure.ts index f9b005ad2fc49909653aa2eeb79322dd1f31c370..e6e93062983fa24cae1ee5d54e281cc3db41c7e2 100644 --- a/src/mol-model-formats/structure/pdb/secondary-structure.ts +++ b/src/mol-model-formats/structure/pdb/secondary-structure.ts @@ -7,6 +7,7 @@ import { CifCategory, CifField } from '../../../mol-io/reader/cif'; import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif'; import { Tokens } from '../../../mol-io/reader/common/text/tokenizer'; +import { Column } from '../../../mol-data/db'; const HelixTypes: {[k: string]: mmCIF_Schema['struct_conf']['conf_type_id']['T']} = { // CLASS NUMBER @@ -99,29 +100,27 @@ export function parseHelix(lines: Tokens, lineStart: number, lineEnd: number): C const beg_auth_asym_id = CifField.ofStrings(helices.map(h => h.initChainID)); const beg_auth_comp_id = CifField.ofStrings(helices.map(h => h.initResName)); - const beg_auth_seq_id = CifField.ofStrings(helices.map(h => h.initSeqNum)); const end_auth_asym_id = CifField.ofStrings(helices.map(h => h.endChainID)); - const end_auth_comp_id = CifField.ofStrings(helices.map(h => h.endResName)); - const end_auth_seq_id = CifField.ofStrings(helices.map(h => h.endSeqNum)); + const end_auth_comp_id = CifField.ofStrings(helices.map(h => h.endResName));; const struct_conf: CifCategory.Fields<mmCIF_Schema['struct_conf']> = { beg_label_asym_id: beg_auth_asym_id, beg_label_comp_id: beg_auth_comp_id, - beg_label_seq_id: beg_auth_seq_id, + beg_label_seq_id: CifField.ofUndefined(helices.length, Column.Schema.int), beg_auth_asym_id, beg_auth_comp_id, - beg_auth_seq_id, + beg_auth_seq_id: CifField.ofStrings(helices.map(h => h.initSeqNum)), conf_type_id: CifField.ofStrings(helices.map(h => getStructConfTypeId(h.helixClass))), details: CifField.ofStrings(helices.map(h => h.comment)), end_label_asym_id: end_auth_asym_id, - end_label_comp_id: end_auth_asym_id, - end_label_seq_id: end_auth_seq_id, + end_label_comp_id: end_auth_comp_id, + end_label_seq_id: CifField.ofUndefined(helices.length, Column.Schema.int), end_auth_asym_id, end_auth_comp_id, - end_auth_seq_id, + end_auth_seq_id: CifField.ofStrings(helices.map(h => h.endSeqNum)), id: CifField.ofStrings(helices.map(h => h.serNum)), pdbx_beg_PDB_ins_code: CifField.ofStrings(helices.map(h => h.initICode)), diff --git a/src/mol-model-formats/structure/property/secondary-structure.ts b/src/mol-model-formats/structure/property/secondary-structure.ts index 616cd18bf2a6fbbf5eabe1a7475b6f1914537467..3bf1f068554faeef60f2e2ed05afe882723c0e84 100644 --- a/src/mol-model-formats/structure/property/secondary-structure.ts +++ b/src/mol-model-formats/structure/property/secondary-structure.ts @@ -62,8 +62,8 @@ type SecondaryStructureData = { type: SecondaryStructureType[], key: number[], e function addHelices(cat: StructConf, map: SecondaryStructureMap, elements: SecondaryStructure.Element[]) { if (!cat._rowCount) return; - const { beg_label_asym_id, beg_label_seq_id, pdbx_beg_PDB_ins_code } = cat; - const { end_label_seq_id, pdbx_end_PDB_ins_code } = cat; + const { beg_label_asym_id, beg_auth_seq_id, pdbx_beg_PDB_ins_code } = cat; + const { end_auth_seq_id, pdbx_end_PDB_ins_code } = cat; const { pdbx_PDB_helix_class, conf_type_id, details } = cat; for (let i = 0, _i = cat._rowCount; i < _i; i++) { @@ -81,9 +81,9 @@ function addHelices(cat: StructConf, map: SecondaryStructureMap, elements: Secon details: details.valueKind(i) === Column.ValueKind.Present ? details.value(i) : void 0 }; const entry: SecondaryStructureEntry = { - startSeqNumber: beg_label_seq_id.value(i), + startSeqNumber: beg_auth_seq_id.value(i), startInsCode: pdbx_beg_PDB_ins_code.value(i), - endSeqNumber: end_label_seq_id.value(i), + endSeqNumber: end_auth_seq_id.value(i), endInsCode: pdbx_end_PDB_ins_code.value(i), type, key: elements.length @@ -108,8 +108,8 @@ function addHelices(cat: StructConf, map: SecondaryStructureMap, elements: Secon function addSheets(cat: StructSheetRange, map: SecondaryStructureMap, sheetCount: number, elements: SecondaryStructure.Element[]) { if (!cat._rowCount) return; - const { beg_label_asym_id, beg_label_seq_id, pdbx_beg_PDB_ins_code } = cat; - const { end_label_seq_id, pdbx_end_PDB_ins_code } = cat; + const { beg_label_asym_id, beg_auth_seq_id, pdbx_beg_PDB_ins_code } = cat; + const { end_auth_seq_id, pdbx_end_PDB_ins_code } = cat; const { sheet_id } = cat; const sheet_id_key = new Map<string, number>(); @@ -132,9 +132,9 @@ function addSheets(cat: StructSheetRange, map: SecondaryStructureMap, sheetCount symmetry: void 0 }; const entry: SecondaryStructureEntry = { - startSeqNumber: beg_label_seq_id.value(i), + startSeqNumber: beg_auth_seq_id.value(i), startInsCode: pdbx_beg_PDB_ins_code.value(i), - endSeqNumber: end_label_seq_id.value(i), + endSeqNumber: end_auth_seq_id.value(i), endInsCode: pdbx_end_PDB_ins_code.value(i), type, key: elements.length @@ -159,12 +159,12 @@ function addSheets(cat: StructSheetRange, map: SecondaryStructureMap, sheetCount } function assignSecondaryStructureEntry(hierarchy: AtomicHierarchy, entry: SecondaryStructureEntry, resStart: ResidueIndex, resEnd: ResidueIndex, data: SecondaryStructureData) { - const { label_seq_id, pdbx_PDB_ins_code } = hierarchy.residues; + const { auth_seq_id, pdbx_PDB_ins_code } = hierarchy.residues; const { endSeqNumber, endInsCode, key, type } = entry; let rI = resStart; while (rI < resEnd) { - const seqNumber = label_seq_id.value(rI); + const seqNumber = auth_seq_id.value(rI); data.type[rI] = type; data.key[rI] = key; @@ -180,7 +180,7 @@ function assignSecondaryStructureEntry(hierarchy: AtomicHierarchy, entry: Second function assignSecondaryStructureRanges(hierarchy: AtomicHierarchy, map: SecondaryStructureMap, data: SecondaryStructureData) { const { count: chainCount } = hierarchy.chainAtomSegments; const { label_asym_id } = hierarchy.chains; - const { label_seq_id, pdbx_PDB_ins_code } = hierarchy.residues; + const { auth_seq_id, pdbx_PDB_ins_code } = hierarchy.residues; for (let cI = 0 as ChainIndex; cI < chainCount; cI++) { const resStart = AtomicHierarchy.chainStartResidueIndex(hierarchy, cI), resEnd = AtomicHierarchy.chainEndResidueIndexExcl(hierarchy, cI); @@ -189,7 +189,7 @@ function assignSecondaryStructureRanges(hierarchy: AtomicHierarchy, map: Seconda const entries = map.get(asymId)!; for (let rI = resStart; rI < resEnd; rI++) { - const seqNumber = label_seq_id.value(rI); + const seqNumber = auth_seq_id.value(rI); if (entries.has(seqNumber)) { const entryList = entries.get(seqNumber)!; for (const entry of entryList) { diff --git a/src/mol-model/structure/export/categories/atom_site.ts b/src/mol-model/structure/export/categories/atom_site.ts index 1013bbaa221bbfe474bbad780d3be4d1829c557b..3f8354fb354388e6922eafa9cb432b2b93384708 100644 --- a/src/mol-model/structure/export/categories/atom_site.ts +++ b/src/mol-model/structure/export/categories/atom_site.ts @@ -52,6 +52,7 @@ const atom_site_fields = CifWriter.fields<StructureElement.Location, Structure>( .float('Cartn_y', P.atom.y, { digitCount: 3, encoder: E.fixedPoint3 }) .float('Cartn_z', P.atom.z, { digitCount: 3, encoder: E.fixedPoint3 }) .float('occupancy', P.atom.occupancy, { digitCount: 2, encoder: E.fixedPoint2 }) + .float('B_iso_or_equiv', P.atom.B_iso_or_equiv, { digitCount: 2, encoder: E.fixedPoint2 }) .int('pdbx_formal_charge', P.atom.pdbx_formal_charge, { encoder: E.deltaRLE, valueKind: (k, d) => k.unit.model.atomicHierarchy.atoms.pdbx_formal_charge.valueKind(k.element) diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts index 62d0c844f7155422b8509b336f5947628dd00786..d8f36ff6837b308fbabebdc40e84f59c1382c416 100644 --- a/src/mol-model/structure/structure/properties.ts +++ b/src/mol-model/structure/structure/properties.ts @@ -52,7 +52,7 @@ const atom = { auth_atom_id: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_atom_id.value(l.element)), label_alt_id: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_alt_id.value(l.element)), label_comp_id: p(compId), - auth_comp_id: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_comp_id.value(l.unit.residueIndex[l.element])), + auth_comp_id: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_comp_id.value(l.element)), pdbx_formal_charge: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.pdbx_formal_charge.value(l.element)), // Derived @@ -93,7 +93,7 @@ const residue = { pdbx_PDB_ins_code: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.element])), // Properties - isNonStandard: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : microheterogeneityCompIds(l).some(c => l.unit.model.properties.chemicalComponentMap.get(c)!.mon_nstd_flag[0] !== 'y')), + isNonStandard: p(l => !Unit.isAtomic(l.unit) ? notAtomic() : microheterogeneityCompIds(l).some(c => l.unit.model.properties.chemicalComponentMap.get(c)!.mon_nstd_flag[0] === 'n')), hasMicroheterogeneity: p(hasMicroheterogeneity), microheterogeneityCompIds: p(microheterogeneityCompIds), secondary_structure_type: p(l => { diff --git a/src/mol-plugin-state/builder/structure/hierarchy-preset.ts b/src/mol-plugin-state/builder/structure/hierarchy-preset.ts index d220f1f1b8b27cf6e7096e01912d0d418aaa4830..e007906d1c82084e0cbe60bfacf235dce91a1c86 100644 --- a/src/mol-plugin-state/builder/structure/hierarchy-preset.ts +++ b/src/mol-plugin-state/builder/structure/hierarchy-preset.ts @@ -16,6 +16,7 @@ import { PluginContext } from '../../../mol-plugin/context'; import { Vec3 } from '../../../mol-math/linear-algebra'; import { Model } from '../../../mol-model/structure'; import { getStructureQuality } from '../../../mol-repr/util'; +import { OperatorNameColorThemeProvider } from '../../../mol-theme/color/operator-name'; export interface TrajectoryHierarchyPresetProvider<P = any, S = {}> extends PresetProvider<PluginStateObject.Molecule.Trajectory, P, S> { } export function TrajectoryHierarchyPresetProvider<P, S>(preset: TrajectoryHierarchyPresetProvider<P, S>) { return preset; } @@ -100,7 +101,7 @@ const allModels = TrajectoryHierarchyPresetProvider({ structures.push(structure); const quality = structure.obj ? getStructureQuality(structure.obj.data, { elementCountFactor: tr.length }) : 'medium'; - await builder.representation.applyPreset(structureProperties, params.representationPreset || 'auto', { globalThemeName: 'model-index', quality }); + await builder.representation.applyPreset(structureProperties, params.representationPreset || 'auto', { theme: { globalName: 'model-index' }, quality }); } return { models, structures }; @@ -125,7 +126,7 @@ async function applyCrystalSymmetry(props: { ijkMin: Vec3, ijkMax: Vec3, theme?: const structureProperties = await builder.insertStructureProperties(structure, params.structureProperties); const unitcell = await builder.tryCreateUnitcell(modelProperties, undefined, { isHidden: false }); - const representation = await plugin.builders.structure.representation.applyPreset(structureProperties, params.representationPreset || 'auto', { globalThemeName: props.theme }); + const representation = await plugin.builders.structure.representation.applyPreset(structureProperties, params.representationPreset || 'auto', { theme: { globalName: props.theme } }); return { model, @@ -167,10 +168,52 @@ const supercell = TrajectoryHierarchyPresetProvider({ } }); +const CrystalContactsParams = (a: PluginStateObject.Molecule.Trajectory | undefined, plugin: PluginContext) => ({ + model: PD.Optional(PD.Group(StateTransformer.getParamDefinition(StateTransforms.Model.ModelFromTrajectory, a, plugin))), + ...CommonParams(a, plugin) +}); + +const crystalContacts = TrajectoryHierarchyPresetProvider({ + id: 'preset-trajectory-crystal-contacts', + display: { + name: 'Crystal Contacts', group: 'Preset', + description: 'Showsasymetric unit and chains from neighbours within 5 \u212B, i.e., symmetry mates.' + }, + isApplicable: o => { + return Model.hasCrystalSymmetry(o.data[0]); + }, + params: CrystalContactsParams, + async apply(trajectory, params, plugin) { + const builder = plugin.builders.structure; + + const model = await builder.createModel(trajectory, params.model); + const modelProperties = await builder.insertModelProperties(model, params.modelProperties); + + const structure = await builder.createStructure(modelProperties || model, { + name: 'symmetry-mates', + params: { radius: 5 } + }); + const structureProperties = await builder.insertStructureProperties(structure, params.structureProperties); + + const unitcell = await builder.tryCreateUnitcell(modelProperties, undefined, { isHidden: true }); + const representation = await plugin.builders.structure.representation.applyPreset(structureProperties, params.representationPreset || 'auto', { theme: { globalName: 'operator-name', carbonColor: 'operator-name', focus: { name: 'element-symbol', params: { carbonColor: { name: 'operator-name', params: OperatorNameColorThemeProvider.defaultValues } } } } }); + + return { + model, + modelProperties, + unitcell, + structure, + structureProperties, + representation + }; + } +}); + export const PresetTrajectoryHierarchy = { 'default': defaultPreset, 'all-models': allModels, unitcell, supercell, + crystalContacts, }; export type PresetTrajectoryHierarchy = typeof PresetTrajectoryHierarchy; \ No newline at end of file diff --git a/src/mol-plugin-state/builder/structure/representation-preset.ts b/src/mol-plugin-state/builder/structure/representation-preset.ts index 3eb9bdf539867ef6e5057fcd4afcd37a877ac820..d8d83bbdc685a29b92c8ccd33121df77871cb057 100644 --- a/src/mol-plugin-state/builder/structure/representation-preset.ts +++ b/src/mol-plugin-state/builder/structure/representation-preset.ts @@ -18,6 +18,8 @@ import { StructureSelectionQueries as Q } from '../../helpers/structure-selectio import { PluginConfig } from '../../../mol-plugin/config'; import { StructureFocusRepresentation } from '../../../mol-plugin/behavior/dynamic/selection/structure-focus-representation'; import { createStructureColorThemeParams } from '../../helpers/structure-representation-params'; +import { ChainIdColorThemeProvider } from '../../../mol-theme/color/chain-id'; +import { OperatorNameColorThemeProvider } from '../../../mol-theme/color/operator-name'; export interface StructureRepresentationPresetProvider<P = any, S extends _Result = _Result> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { } export function StructureRepresentationPresetProvider<P, S extends _Result>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; } @@ -34,7 +36,7 @@ export namespace StructureRepresentationPresetProvider { quality: PD.Optional(PD.Select<VisualQuality>('auto', VisualQualityOptions)), theme: PD.Optional(PD.Group({ globalName: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')), - carbonByChainId: PD.Optional(PD.Boolean(true)), + carbonColor: PD.Optional(PD.Select('chain-id', PD.arrayToOptions(['chain-id', 'operator-name', 'element-symbol'] as const))), focus: PD.Optional(PD.Group({ name: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')), params: PD.Optional(PD.Value<ColorTheme.BuiltInParams<ColorTheme.BuiltIn>>({} as any)) @@ -43,6 +45,14 @@ export namespace StructureRepresentationPresetProvider { }; export type CommonParams = PD.ValuesFor<typeof CommonParams> + function getCarbonColorParams(name: 'chain-id' | 'operator-name' | 'element-symbol') { + return name === 'chain-id' + ? { name, params: ChainIdColorThemeProvider.defaultValues } + : name === 'operator-name' + ? { name, params: OperatorNameColorThemeProvider.defaultValues } + : { name, params: {} }; + } + export function reprBuilder(plugin: PluginContext, params: CommonParams) { const update = plugin.state.data.build(); const builder = plugin.builders.structure.representation; @@ -53,16 +63,16 @@ export namespace StructureRepresentationPresetProvider { if (params.quality && params.quality !== 'auto') typeParams.quality = params.quality; if (params.ignoreHydrogens !== void 0) typeParams.ignoreHydrogens = !!params.ignoreHydrogens; const color: ColorTheme.BuiltIn | undefined = params.theme?.globalName ? params.theme?.globalName : void 0; - const ballAndStickColor: ColorTheme.BuiltInParams<'element-symbol'> = typeof params.theme?.carbonByChainId !== 'undefined' ? { carbonByChainId: !!params.theme?.carbonByChainId } : { }; + const ballAndStickColor: ColorTheme.BuiltInParams<'element-symbol'> = params.theme?.carbonColor !== undefined + ? { carbonColor: getCarbonColorParams(params.theme?.carbonColor) } + : { }; return { update, builder, color, typeParams, ballAndStickColor }; } export function updateFocusRepr<T extends ColorTheme.BuiltIn>(plugin: PluginContext, structure: Structure, themeName: T | undefined, themeParams: ColorTheme.BuiltInParams<T> | undefined) { - if (!themeName && !themeParams) return; - return plugin.state.updateBehavior(StructureFocusRepresentation, p => { - const c = createStructureColorThemeParams(plugin, structure, 'ball-and-stick', themeName, themeParams); + const c = createStructureColorThemeParams(plugin, structure, 'ball-and-stick', themeName || 'element-symbol', themeParams); p.surroundingsParams.colorTheme = c; p.targetParams.colorTheme = c; }); @@ -155,8 +165,8 @@ const polymerAndLigand = StructureRepresentationPresetProvider({ branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.3 }, color, colorParams: ballAndStickColor }, { tag: 'branched-ball-and-stick' }), branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }, { tag: 'branched-snfg-3d' }), water: builder.buildRepresentation(update, components.water, { type: waterType, typeParams: { ...typeParams, alpha: 0.6 }, color }, { tag: 'water' }), - ion: builder.buildRepresentation(update, components.ion, { type: 'ball-and-stick', typeParams, color, colorParams: { carbonByChainId: false } }, { tag: 'ion' }), - lipid: builder.buildRepresentation(update, components.lipid, { type: lipidType, typeParams: { ...typeParams, alpha: 0.6 }, color, colorParams: { carbonByChainId: false } }, { tag: 'lipid' }), + ion: builder.buildRepresentation(update, components.ion, { type: 'ball-and-stick', typeParams, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ion' }), + lipid: builder.buildRepresentation(update, components.lipid, { type: lipidType, typeParams: { ...typeParams, alpha: 0.6 }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'lipid' }), coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'chain-id' }, { tag: 'coarse' }) }; diff --git a/src/mol-plugin-state/manager/loci-label.ts b/src/mol-plugin-state/manager/loci-label.ts index 5193912b4bf3ce11ed652f0da69791b1298ba1e3..a5ee1eba6cd8184ea6b7495bd58e06e036e6c938 100644 --- a/src/mol-plugin-state/manager/loci-label.ts +++ b/src/mol-plugin-state/manager/loci-label.ts @@ -22,6 +22,12 @@ export type LociLabelProvider = { export class LociLabelManager { providers: LociLabelProvider[] = []; + clearProviders() { + this.providers = []; + this.isDirty = true; + this.showLabels(); + } + addProvider(provider: LociLabelProvider) { this.providers.push(provider); this.providers.sort((a, b) => (b.priority || 0) - (a.priority || 0)); diff --git a/src/mol-plugin-state/transforms/data.ts b/src/mol-plugin-state/transforms/data.ts index 7c28a8c7b4be3e82d0b25ed8f8a85636641b04cf..178d678e6ce919b4b2f3103ae98c06b69cf93b64 100644 --- a/src/mol-plugin-state/transforms/data.ts +++ b/src/mol-plugin-state/transforms/data.ts @@ -5,7 +5,6 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { isTypedArray } from '../../mol-data/db/column-helpers'; import * as CCP4 from '../../mol-io/reader/ccp4/parser'; import { CIF } from '../../mol-io/reader/cif'; import * as DSN6 from '../../mol-io/reader/dsn6/parser'; @@ -135,18 +134,23 @@ const RawData = PluginStateTransform.BuiltIn({ from: [SO.Root], to: [SO.Data.String, SO.Data.Binary], params: { - data: PD.Value<string | number[]>('', { isHidden: true }), + data: PD.Value<string | number[] | ArrayBuffer | Uint8Array>('', { isHidden: true }), label: PD.Optional(PD.Text('')) } })({ apply({ params: p }) { return Task.create('Raw Data', async () => { - if (typeof p.data !== 'string' && isTypedArray(p.data)) { - throw new Error('Supplied binary data must be a plain array.'); + if (typeof p.data === 'string') { + return new SO.Data.String(p.data as string, { label: p.label ? p.label : 'String' }); + } else if (Array.isArray(p.data)) { + return new SO.Data.Binary(new Uint8Array(p.data), { label: p.label ? p.label : 'Binary' }); + } else if (p.data instanceof ArrayBuffer) { + return new SO.Data.Binary(new Uint8Array(p.data), { label: p.label ? p.label : 'Binary' }); + } else if (p.data instanceof Uint8Array) { + return new SO.Data.Binary(p.data, { label: p.label ? p.label : 'Binary' }); + } else { + throw new Error('Supplied binary data must be a plain array, ArrayBuffer, or Uint8Array.'); } - return typeof p.data === 'string' - ? new SO.Data.String(p.data as string, { label: p.label ? p.label : 'String' }) - : new SO.Data.Binary(new Uint8Array(p.data), { label: p.label ? p.label : 'Binary' }); }); }, update({ oldParams, newParams, b }) { @@ -156,6 +160,25 @@ const RawData = PluginStateTransform.BuiltIn({ return StateTransformer.UpdateResult.Updated; } return StateTransformer.UpdateResult.Unchanged; + }, + customSerialization: { + toJSON(p) { + if (typeof p.data === 'string' || Array.isArray(p.data)) { + return p; + } else if (p.data instanceof ArrayBuffer) { + const v = new Uint8Array(p.data); + const data = new Array(v.length); + for (let i = 0, _i = v.length; i < _i; i++) data[i] = v[i]; + return { data, label: p.label }; + } else if (p.data instanceof Uint8Array) { + const data = new Array(p.data.length); + for (let i = 0, _i = p.data.length; i < _i; i++) data[i] = p.data[i]; + return { data, label: p.label }; + } + }, + fromJSON(data: any) { + return data; + } } }); diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 9450a2469678aa9339982aa020a7cfb33a6a84e5..eb7fc71008d9c2f481997477a52e9ed2ce0e0d0e 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -182,8 +182,7 @@ export class PluginContext { (this.canvas3d as Canvas3D) = Canvas3D.fromCanvas(canvas); this.canvas3dInit.next(true); - const renderer = this.canvas3d!.props.renderer; - PluginCommands.Canvas3D.SetSettings(this, { settings: { renderer: { ...renderer, backgroundColor: Color(0xFCFBF9) } } }); + this.canvas3d?.setProps(this.spec.components?.viewport?.canvas3d || { renderer: { backgroundColor: Color(0xFCFBF9) } }); this.canvas3d!.animate(); (this.helpers.viewportScreenshot as ViewportScreenshotHelper) = new ViewportScreenshotHelper(this); return true; @@ -346,29 +345,31 @@ export class PluginContext { } } - constructor(public spec: PluginSpec) { - // the reason for this is that sometimes, transform params get modified inline (i.e. palette.valueLabel) - // and freezing the params object causes "read-only exception" - // TODO: is this the best place to do it? - setAutoFreeze(false); - + async init() { this.events.log.subscribe(e => this.log.entries = this.log.entries.push(e)); this.initBehaviorEvents(); this.initBuiltInBehavior(); - this.initBehaviors(); + (this.managers.interactivity as InteractivityManager) = new InteractivityManager(this); + (this.managers.lociLabels as LociLabelManager) = new LociLabelManager(this); + (this.builders.structure as StructureBuilder) = new StructureBuilder(this); + this.initDataActions(); this.initAnimations(); this.initCustomParamEditors(); - (this.managers.interactivity as InteractivityManager) = new InteractivityManager(this); - (this.managers.lociLabels as LociLabelManager) = new LociLabelManager(this); - - (this.builders.structure as StructureBuilder) = new StructureBuilder(this); + await this.initBehaviors(); this.log.message(`Mol* Plugin ${PLUGIN_VERSION} [${PLUGIN_VERSION_DATE.toLocaleString()}]`); if (!isProductionMode) this.log.message(`Development mode enabled`); if (isDebugMode) this.log.message(`Debug mode enabled`); } + + constructor(public spec: PluginSpec) { + // the reason for this is that sometimes, transform params get modified inline (i.e. palette.valueLabel) + // and freezing the params object causes "read-only exception" + // TODO: is this the best place to do it? + setAutoFreeze(false); + } } \ No newline at end of file diff --git a/src/mol-plugin/index.ts b/src/mol-plugin/index.ts index c1b568029e0dcbe7676fbffd70d54cac011a6475..288db73b74ac69038d1b8f4ebad103cb5730abd1 100644 --- a/src/mol-plugin/index.ts +++ b/src/mol-plugin/index.ts @@ -94,6 +94,16 @@ export const DefaultPluginSpec: PluginSpec = { export function createPlugin(target: HTMLElement, spec?: PluginSpec): PluginContext { const ctx = new PluginContext(spec || DefaultPluginSpec); + ctx.init(); ReactDOM.render(React.createElement(Plugin, { plugin: ctx }), target); return ctx; +} + +/** Returns the instance of the plugin after all behaviors have been initialized */ +export async function createPluginAsync(target: HTMLElement, spec?: PluginSpec) { + const ctx = new PluginContext(spec || DefaultPluginSpec); + const init = ctx.init(); + ReactDOM.render(React.createElement(Plugin, { plugin: ctx }), target); + await init; + return ctx; } \ No newline at end of file diff --git a/src/mol-plugin/spec.ts b/src/mol-plugin/spec.ts index 0b6e08d118e8255b23ea425bf2884482f18f4fd5..437dca7679402518ba175a58d33ed3e7b737c3a3 100644 --- a/src/mol-plugin/spec.ts +++ b/src/mol-plugin/spec.ts @@ -10,6 +10,7 @@ import { StateTransformParameters } from '../mol-plugin-ui/state/common'; import { PluginLayoutStateProps } from './layout'; import { PluginStateAnimation } from '../mol-plugin-state/animation/model'; import { PluginConfigItem } from './config'; +import { PartialCanvas3DProps } from '../mol-canvas3d/canvas3d'; export { PluginSpec }; @@ -27,7 +28,8 @@ interface PluginSpec { structureTools?: React.ComponentClass, viewport?: { view?: React.ComponentClass, - controls?: React.ComponentClass + controls?: React.ComponentClass, + canvas3d?: PartialCanvas3DProps } }, config?: [PluginConfigItem, unknown][] diff --git a/src/mol-state/transform.ts b/src/mol-state/transform.ts index c1cce1fa255569073595944ae90969038e390d9f..536fa51ba0120bbe6bd0e5c3acac6f9e88976d44 100644 --- a/src/mol-state/transform.ts +++ b/src/mol-state/transform.ts @@ -194,7 +194,7 @@ namespace Transform { export function fromJSON(t: Serialized): Transform { const transformer = StateTransformer.get(t.transformer); const pFromJson = transformer.definition.customSerialization - ? transformer.definition.customSerialization.toJSON + ? transformer.definition.customSerialization.fromJSON : _id; return { parent: t.parent as Ref, diff --git a/src/mol-theme/color/chain-id.ts b/src/mol-theme/color/chain-id.ts index 18e3eb2a20d8a704c814c80c00170a02b841bc1e..990aaa1034f3cfe2372244270a94eaefe7f67a98 100644 --- a/src/mol-theme/color/chain-id.ts +++ b/src/mol-theme/color/chain-id.ts @@ -24,6 +24,9 @@ export const ChainIdColorThemeParams = { export type ChainIdColorThemeParams = typeof ChainIdColorThemeParams export function getChainIdColorThemeParams(ctx: ThemeDataContext) { const params = PD.clone(ChainIdColorThemeParams); + if (ctx.structure?.models.some(m => m.coarseHierarchy.isDefined)) { + params.asymId.defaultValue = 'label'; + } return params; } diff --git a/src/mol-theme/color/element-symbol.ts b/src/mol-theme/color/element-symbol.ts index c0a96088e1f8778dbea90316635bbff088d53902..4f21b908ae02dadbefccb0e9b1d21f88e75c54f9 100644 --- a/src/mol-theme/color/element-symbol.ts +++ b/src/mol-theme/color/element-symbol.ts @@ -13,7 +13,8 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { ThemeDataContext } from '../theme'; import { TableLegend } from '../../mol-util/legend'; import { getAdjustedColorMap } from '../../mol-util/color/color'; -import { ChainIdColorTheme, getChainIdColorThemeParams } from './chain-id'; +import { ChainIdColorTheme, ChainIdColorThemeParams } from './chain-id'; +import { OperatorNameColorThemeParams, OperatorNameColorTheme } from './operator-name'; // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF) export const ElementSymbolColors = ColorMap({ @@ -24,8 +25,14 @@ export type ElementSymbolColors = typeof ElementSymbolColors const DefaultElementSymbolColor = Color(0xFFFFFF); const Description = 'Assigns a color to every atom according to its chemical element.'; +// TODO generalise `carbonColor` param to all themes? + export const ElementSymbolColorThemeParams = { - carbonByChainId: PD.Boolean(true), + carbonColor: PD.MappedStatic('chain-id', { + 'chain-id': PD.Group({ ...ChainIdColorThemeParams }), + 'operator-name': PD.Group({ ...OperatorNameColorThemeParams }), + 'element-symbol': PD.Group({}) + }, { description: 'Use chain-id coloring for carbon atoms.' }), saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }), lightness: PD.Numeric(0.2, { min: -6, max: 6, step: 0.1 }) }; @@ -42,11 +49,15 @@ export function elementSymbolColor(colorMap: ElementSymbolColors, element: Eleme export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<ElementSymbolColorThemeParams>): ColorTheme<ElementSymbolColorThemeParams> { const colorMap = getAdjustedColorMap(ElementSymbolColors, props.saturation, props.lightness); - const chainIdColor = ChainIdColorTheme(ctx, PD.getDefaultValues(getChainIdColorThemeParams(ctx))).color; + const carbonColor = props.carbonColor.name === 'chain-id' + ? ChainIdColorTheme(ctx, props.carbonColor.params).color + : props.carbonColor.name === 'operator-name' + ? OperatorNameColorTheme(ctx, props.carbonColor.params).color + : undefined; function elementColor(element: ElementSymbol, location: Location) { - return (props.carbonByChainId && element === 'C') - ? chainIdColor(location, false) + return (carbonColor && element === 'C') + ? carbonColor(location, false) : elementSymbolColor(colorMap, element); } @@ -68,7 +79,7 @@ export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values< return { factory: ElementSymbolColorTheme, - granularity: 'group', + granularity: 'groupInstance', color, props, description: Description, diff --git a/src/mol-theme/color/illustrative.ts b/src/mol-theme/color/illustrative.ts index 9f53e45a203b6e3d32e11a6cf37aa98d3fd3624e..d756df828adf5034d6efd7a9da7f76e851d7164c 100644 --- a/src/mol-theme/color/illustrative.ts +++ b/src/mol-theme/color/illustrative.ts @@ -11,19 +11,21 @@ import { Location } from '../../mol-model/location'; import { ColorTheme } from '../color'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { ThemeDataContext } from '../theme'; -import { ChainIdColorTheme, getChainIdColorThemeParams } from './chain-id'; +import { ChainIdColorTheme, ChainIdColorThemeParams } from './chain-id'; const DefaultIllustrativeColor = Color(0xEEEEEE); const Description = `Assigns an illustrative color that gives every chain a unique color with lighter carbons (inspired by David Goodsell's Molecule of the Month style).`; -export const IllustrativeColorThemeParams = {}; +export const IllustrativeColorThemeParams = { + ...ChainIdColorThemeParams +}; export type IllustrativeColorThemeParams = typeof IllustrativeColorThemeParams export function getIllustrativeColorThemeParams(ctx: ThemeDataContext) { return IllustrativeColorThemeParams; // TODO return copy } export function IllustrativeColorTheme(ctx: ThemeDataContext, props: PD.Values<IllustrativeColorThemeParams>): ColorTheme<IllustrativeColorThemeParams> { - const { color: chainIdColor, legend } = ChainIdColorTheme(ctx, PD.getDefaultValues(getChainIdColorThemeParams(ctx))); + const { color: chainIdColor, legend } = ChainIdColorTheme(ctx, props); function illustrativeColor(location: Location, typeSymbol: ElementSymbol) { const baseColor = chainIdColor(location, false); diff --git a/src/mol-theme/color/operator-name.ts b/src/mol-theme/color/operator-name.ts index 1e1b91ac34dd0c0eb9f2c75b1e1b0e240c81f3cf..e6e2f432d9f8c383d6fe17fe73be1ecd525a1e47 100644 --- a/src/mol-theme/color/operator-name.ts +++ b/src/mol-theme/color/operator-name.ts @@ -12,9 +12,8 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { ThemeDataContext } from '../theme'; import { getPaletteParams, getPalette } from '../../mol-util/color/palette'; import { ScaleLegend, TableLegend } from '../../mol-util/legend'; -import { ColorLists, getColorListFromName } from '../../mol-util/color/lists'; -const DefaultList = 'dark-2'; +const DefaultList = 'many-distinct'; const DefaultColor = Color(0xCCCCCC); const Description = `Assigns a color based on the operator name of a transformed chain.`; @@ -24,15 +23,6 @@ export const OperatorNameColorThemeParams = { export type OperatorNameColorThemeParams = typeof OperatorNameColorThemeParams export function getOperatorNameColorThemeParams(ctx: ThemeDataContext) { const params = PD.clone(OperatorNameColorThemeParams); - if (ctx.structure) { - if (getOperatorNameSerialMap(ctx.structure.root).size > ColorLists[DefaultList].list.length) { - params.palette.defaultValue.name = 'colors'; - params.palette.defaultValue.params = { - ...params.palette.defaultValue.params, - list: { kind: 'interpolate', colors: getColorListFromName(DefaultList).list } - }; - } - } return params; } diff --git a/src/mol-theme/color/sequence-id.ts b/src/mol-theme/color/sequence-id.ts index 0520e0677a6e0fb110a167d39e81cb0f81b371f9..ec262ad0a932b580dc1051ffec21dd925320ae09 100644 --- a/src/mol-theme/color/sequence-id.ts +++ b/src/mol-theme/color/sequence-id.ts @@ -58,9 +58,14 @@ function getSequenceLength(unit: Unit, element: ElementIndex) { break; } if (entityId === '') return 0; + const entityIndex = model.entities.getEntityIndex(entityId); if (entityIndex === -1) return 0; - return model.sequence.byEntityKey[entityIndex].sequence.length; + + const entity = model.sequence.byEntityKey[entityIndex]; + if (entity === undefined) return 0; + + return entity.sequence.length; } export function SequenceIdColorTheme(ctx: ThemeDataContext, props: PD.Values<SequenceIdColorThemeParams>): ColorTheme<SequenceIdColorThemeParams> { @@ -74,15 +79,21 @@ export function SequenceIdColorTheme(ctx: ThemeDataContext, props: PD.Values<Seq const { unit, element } = location; const seq_id = getSeqId(unit, element); if (seq_id > 0) { - scale.setDomain(0, getSequenceLength(unit, element) - 1); - return scale.color(seq_id); + const seqLen = getSequenceLength(unit, element); + if (seqLen) { + scale.setDomain(0, seqLen - 1); + return scale.color(seq_id); + } } } else if (Bond.isLocation(location)) { const { aUnit, aIndex } = location; const seq_id = getSeqId(aUnit, aUnit.elements[aIndex]); if (seq_id > 0) { - scale.setDomain(0, getSequenceLength(aUnit, aUnit.elements[aIndex]) - 1); - return scale.color(seq_id); + const seqLen = getSequenceLength(aUnit, aUnit.elements[aIndex]); + if (seqLen) { + scale.setDomain(0, seqLen - 1); + return scale.color(seq_id); + } } } return DefaultColor; diff --git a/src/servers/volume/server/web-api.ts b/src/servers/volume/server/web-api.ts index 845c18a52f47c0767f4012d556ce48997d100cf3..26baf0ee328e522a8b6cdeb723155417243fb038 100644 --- a/src/servers/volume/server/web-api.ts +++ b/src/servers/volume/server/web-api.ts @@ -153,7 +153,7 @@ function getQueryParams(req: express.Request, isCell: boolean): Data.QueryParams const a = [+req.params.a1, +req.params.a2, +req.params.a3]; const b = [+req.params.b1, +req.params.b2, +req.params.b3]; - const detail = Math.min(Math.max(0, (+req.query.detail) | 0), LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel.length - 1); + const detail = Math.min(Math.max(0, (+req.query.detail!) | 0), LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel.length - 1); const isCartesian = (req.query.space as string || '').toLowerCase() !== 'fractional'; const box: Data.QueryParamsBox = isCell diff --git a/webpack.config.js b/webpack.config.js index f4cdb9f476a93b24a76405c2f722c92cc6b226cb..47813da69b9151f09f5d267436f96e88027c931d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,6 @@ const { createApp, createExample, createBrowserTest } = require('./webpack.config.common.js'); -const examples = ['proteopedia-wrapper', 'basic-wrapper', 'lighting', 'docking-viewer']; +const examples = ['proteopedia-wrapper', 'basic-wrapper', 'lighting']; const tests = [ 'font-atlas', 'marching-cubes', @@ -10,6 +10,7 @@ const tests = [ module.exports = [ createApp('viewer', 'molstar'), + createApp('docking-viewer', 'molstar'), ...examples.map(createExample), ...tests.map(createBrowserTest) ] \ No newline at end of file diff --git a/webpack.config.production.js b/webpack.config.production.js index a5298d65ed0430b7bde4cf6587df78833146c389..94952ee5519c428d0a33dba676142cc6582d1d28 100644 --- a/webpack.config.production.js +++ b/webpack.config.production.js @@ -4,5 +4,6 @@ const examples = ['proteopedia-wrapper', 'basic-wrapper', 'lighting']; module.exports = [ createApp('viewer', 'molstar'), + createApp('docking-viewer', 'molstar'), ...examples.map(createExample) ] \ No newline at end of file