@molstudio/core 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,11 @@ export declare class MolStudio {
51
51
  loadVolume({ data, label, format }: LoadDataOptions<VolumeFormat>): Promise<void>;
52
52
  loadVolumeFromUrl(url: string, label: string, format: VolumeFormat, isBinary: boolean): Promise<void>;
53
53
  private applyStructurePreset;
54
+ /**
55
+ * Mol*'s default preset colors ligand carbons by chain, the same color as the protein around them;
56
+ * element colors keep a ligand distinct from its binding pocket.
57
+ */
58
+ private colorLigandCarbonsByElement;
54
59
  private applyVolumeVisuals;
55
60
  setVisible(id: string, visible: boolean): void;
56
61
  /**
@@ -113,6 +118,10 @@ export declare class MolStudio {
113
118
  private transparencyLayers;
114
119
  private partLoci;
115
120
  private residueRef;
121
+ /**
122
+ * Fits the visible structures, or everything when none is shown. A map's box is usually much larger
123
+ * than its density, so fitting it would leave a fitted model small in the middle of the view.
124
+ */
116
125
  resetCamera(): void;
117
126
  setSpinning(spinning: boolean): void;
118
127
  setBackground(hex: string): void;
package/dist/MolStudio.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { PLDDTConfidenceColorThemeProvider } from "molstar/lib/extensions/model-archive/quality-assessment/color/plddt";
2
+ import { QualityAssessment } from "molstar/lib/extensions/model-archive/quality-assessment/prop";
3
+ import { MmcifFormat } from "molstar/lib/mol-model-formats/structure/mmcif";
2
4
  import { DownloadDensity } from "molstar/lib/mol-plugin-state/actions/volume";
3
5
  import { PluginConfig } from "molstar/lib/mol-plugin/config";
4
6
  import { Volume } from "molstar/lib/mol-model/volume";
5
7
  import { Loci } from "molstar/lib/mol-model/loci";
6
- import { Structure, StructureElement } from "molstar/lib/mol-model/structure";
8
+ import { Model, Structure, StructureElement } from "molstar/lib/mol-model/structure";
7
9
  import { createStructureColorThemeParams, createStructureRepresentationParams, } from "molstar/lib/mol-plugin-state/helpers/structure-representation-params";
8
10
  import { clearStructureTransparency, setStructureTransparency } from "molstar/lib/mol-plugin-state/helpers/structure-transparency";
9
11
  import { createVolumeColorThemeParams } from "molstar/lib/mol-plugin-state/helpers/volume-representation-params";
@@ -72,7 +74,12 @@ export class MolStudio {
72
74
  static async create(target) {
73
75
  const spec = DefaultPluginSpec();
74
76
  // Mol*'s "auto" mode renders at 1x on mobile browsers, which blurs structures on high-DPI phones.
75
- spec.config = [...(spec.config ?? []), [PluginConfig.General.ResolutionMode, "native"]];
77
+ // Mol* refuses software WebGL by default; it is slow but renders in VMs, remote desktops and GPU-less CI.
78
+ spec.config = [
79
+ ...(spec.config ?? []),
80
+ [PluginConfig.General.ResolutionMode, "native"],
81
+ [PluginConfig.General.AllowMajorPerformanceCaveat, true],
82
+ ];
76
83
  const plugin = new PluginContext(spec);
77
84
  await plugin.init();
78
85
  // Only the pLDDT theme is needed; Mol*'s quality-assessment behaviour also pulls in its React UI.
@@ -227,6 +234,22 @@ export class MolStudio {
227
234
  if (!result?.structure.data?.elementCount) {
228
235
  throw new Error(`The ${format} data contains no atoms.`);
229
236
  }
237
+ await this.colorLigandCarbonsByElement(result.structure.ref);
238
+ }
239
+ /**
240
+ * Mol*'s default preset colors ligand carbons by chain, the same color as the protein around them;
241
+ * element colors keep a ligand distinct from its binding pocket.
242
+ */
243
+ async colorLigandCarbonsByElement(structureRef) {
244
+ const structure = this.requireStructure(structureRef);
245
+ const reprs = structure.components
246
+ .filter((component) => staticComponentType(component) === "ligand")
247
+ .flatMap((component) => component.representations)
248
+ .filter((repr) => repr.cell.transform.params?.colorTheme.name === "element-symbol");
249
+ await this.updateEach(reprs, (old) => ({
250
+ ...old,
251
+ colorTheme: { ...old.colorTheme, params: { ...old.colorTheme.params, carbonColor: { name: "element-symbol", params: {} } } },
252
+ }));
230
253
  }
231
254
  async applyVolumeVisuals(raw, format) {
232
255
  const provider = this.plugin.dataFormats.get(format);
@@ -380,7 +403,7 @@ export class MolStudio {
380
403
  ? structure.components.flatMap((component) => component.representations)
381
404
  : (volume?.representations ?? this.requireComponent(target).representations);
382
405
  const renderObjects = reprs.flatMap((repr) => repr.cell.obj?.data.repr.renderObjects ?? []);
383
- this.plugin.managers.camera.focusRenderObjects(renderObjects);
406
+ this.plugin.managers.camera.focusRenderObjects(renderObjects, FOCUS_OPTIONS);
384
407
  }
385
408
  async remove(id) {
386
409
  await PluginCommands.State.RemoveObject(this.plugin, {
@@ -408,7 +431,7 @@ export class MolStudio {
408
431
  const loci = this.partLoci(part);
409
432
  if (part.scope === "residue")
410
433
  this.plugin.managers.structure.focus.setFromLoci(loci);
411
- this.plugin.managers.camera.focusLoci(loci);
434
+ this.plugin.managers.camera.focusLoci(loci, FOCUS_OPTIONS);
412
435
  }
413
436
  /** Leaves Mol*'s focus view, removing its target and surroundings components, and returns to the earlier view. */
414
437
  clearFocus() {
@@ -562,8 +585,21 @@ export class MolStudio {
562
585
  return owner ? { objectId: owner.cell.transform.ref, residueIndex } : null;
563
586
  }
564
587
  // ---- viewport ----
588
+ /**
589
+ * Fits the visible structures, or everything when none is shown. A map's box is usually much larger
590
+ * than its density, so fitting it would leave a fitted model small in the middle of the view.
591
+ */
565
592
  resetCamera() {
566
- PluginCommands.Camera.Reset(this.plugin, {});
593
+ const renderObjects = this.plugin.managers.structure.hierarchy.current.structures
594
+ .flatMap((structure) => structure.components)
595
+ .flatMap((component) => component.representations)
596
+ .flatMap((repr) => repr.cell.obj?.data.repr.renderObjects ?? [])
597
+ .filter((renderObject) => renderObject.state.visible);
598
+ if (renderObjects.length === 0) {
599
+ PluginCommands.Camera.Reset(this.plugin, {});
600
+ return;
601
+ }
602
+ this.plugin.managers.camera.focusRenderObjects(renderObjects, { extraRadius: 0 });
567
603
  }
568
604
  setSpinning(spinning) {
569
605
  const canvas3d = this.requireCanvas();
@@ -727,8 +763,7 @@ export class MolStudio {
727
763
  }
728
764
  applicableSchemes(ref) {
729
765
  const structure = ref.cell.obj?.data;
730
- const plddt = this.plugin.representation.structure.themes.colorThemeRegistry.get("plddt-confidence");
731
- const hasPlddt = !!structure && !!plddt?.isApplicable({ structure });
766
+ const hasPlddt = !!structure && structure.models.some(isPredictedModel);
732
767
  return hasPlddt ? ["plddt-confidence", ...BASE_SCHEMES] : [...BASE_SCHEMES];
733
768
  }
734
769
  // ---- lookup helpers ----
@@ -841,6 +876,21 @@ const AXES = {
841
876
  };
842
877
  /** Matches Mol*'s default camera focus animation. */
843
878
  const CAMERA_TRANSITION_MS = 250;
879
+ /** Keeps the surroundings of small targets (a ligand, a residue) in view instead of filling the screen with them. */
880
+ const FOCUS_OPTIONS = { minRadius: 14 };
881
+ /**
882
+ * Whether B-factors hold pLDDT scores. Mol* assumes so for any model not marked experimental, but most
883
+ * PDB entries lack that mark, so an entry with experimental methods recorded does not count.
884
+ */
885
+ function isPredictedModel(model) {
886
+ if (QualityAssessment.isApplicable(model, "pLDDT"))
887
+ return true;
888
+ if (!MmcifFormat.is(model.sourceData) || !model.atomicConformation.B_iso_or_equiv.isDefined)
889
+ return false;
890
+ if (Model.isExperimental(model))
891
+ return false;
892
+ return model.sourceData.data.db.exptl.method.rowCount === 0;
893
+ }
844
894
  const FOCUS_TAGS = {
845
895
  [StructureFocusRepresentationTags.TargetSel]: "target",
846
896
  [StructureFocusRepresentationTags.SurrSel]: "surroundings",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molstudio/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "UI-less Mol* controller that exposes the viewer as objects and display operations.",
5
5
  "license": "MIT",
6
6
  "keywords": [