@molstudio/core 0.1.1 → 0.1.2

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";
@@ -227,6 +229,22 @@ export class MolStudio {
227
229
  if (!result?.structure.data?.elementCount) {
228
230
  throw new Error(`The ${format} data contains no atoms.`);
229
231
  }
232
+ await this.colorLigandCarbonsByElement(result.structure.ref);
233
+ }
234
+ /**
235
+ * Mol*'s default preset colors ligand carbons by chain, the same color as the protein around them;
236
+ * element colors keep a ligand distinct from its binding pocket.
237
+ */
238
+ async colorLigandCarbonsByElement(structureRef) {
239
+ const structure = this.requireStructure(structureRef);
240
+ const reprs = structure.components
241
+ .filter((component) => staticComponentType(component) === "ligand")
242
+ .flatMap((component) => component.representations)
243
+ .filter((repr) => repr.cell.transform.params?.colorTheme.name === "element-symbol");
244
+ await this.updateEach(reprs, (old) => ({
245
+ ...old,
246
+ colorTheme: { ...old.colorTheme, params: { ...old.colorTheme.params, carbonColor: { name: "element-symbol", params: {} } } },
247
+ }));
230
248
  }
231
249
  async applyVolumeVisuals(raw, format) {
232
250
  const provider = this.plugin.dataFormats.get(format);
@@ -380,7 +398,7 @@ export class MolStudio {
380
398
  ? structure.components.flatMap((component) => component.representations)
381
399
  : (volume?.representations ?? this.requireComponent(target).representations);
382
400
  const renderObjects = reprs.flatMap((repr) => repr.cell.obj?.data.repr.renderObjects ?? []);
383
- this.plugin.managers.camera.focusRenderObjects(renderObjects);
401
+ this.plugin.managers.camera.focusRenderObjects(renderObjects, FOCUS_OPTIONS);
384
402
  }
385
403
  async remove(id) {
386
404
  await PluginCommands.State.RemoveObject(this.plugin, {
@@ -408,7 +426,7 @@ export class MolStudio {
408
426
  const loci = this.partLoci(part);
409
427
  if (part.scope === "residue")
410
428
  this.plugin.managers.structure.focus.setFromLoci(loci);
411
- this.plugin.managers.camera.focusLoci(loci);
429
+ this.plugin.managers.camera.focusLoci(loci, FOCUS_OPTIONS);
412
430
  }
413
431
  /** Leaves Mol*'s focus view, removing its target and surroundings components, and returns to the earlier view. */
414
432
  clearFocus() {
@@ -562,8 +580,21 @@ export class MolStudio {
562
580
  return owner ? { objectId: owner.cell.transform.ref, residueIndex } : null;
563
581
  }
564
582
  // ---- viewport ----
583
+ /**
584
+ * Fits the visible structures, or everything when none is shown. A map's box is usually much larger
585
+ * than its density, so fitting it would leave a fitted model small in the middle of the view.
586
+ */
565
587
  resetCamera() {
566
- PluginCommands.Camera.Reset(this.plugin, {});
588
+ const renderObjects = this.plugin.managers.structure.hierarchy.current.structures
589
+ .flatMap((structure) => structure.components)
590
+ .flatMap((component) => component.representations)
591
+ .flatMap((repr) => repr.cell.obj?.data.repr.renderObjects ?? [])
592
+ .filter((renderObject) => renderObject.state.visible);
593
+ if (renderObjects.length === 0) {
594
+ PluginCommands.Camera.Reset(this.plugin, {});
595
+ return;
596
+ }
597
+ this.plugin.managers.camera.focusRenderObjects(renderObjects, { extraRadius: 0 });
567
598
  }
568
599
  setSpinning(spinning) {
569
600
  const canvas3d = this.requireCanvas();
@@ -727,8 +758,7 @@ export class MolStudio {
727
758
  }
728
759
  applicableSchemes(ref) {
729
760
  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 });
761
+ const hasPlddt = !!structure && structure.models.some(isPredictedModel);
732
762
  return hasPlddt ? ["plddt-confidence", ...BASE_SCHEMES] : [...BASE_SCHEMES];
733
763
  }
734
764
  // ---- lookup helpers ----
@@ -841,6 +871,21 @@ const AXES = {
841
871
  };
842
872
  /** Matches Mol*'s default camera focus animation. */
843
873
  const CAMERA_TRANSITION_MS = 250;
874
+ /** Keeps the surroundings of small targets (a ligand, a residue) in view instead of filling the screen with them. */
875
+ const FOCUS_OPTIONS = { minRadius: 14 };
876
+ /**
877
+ * Whether B-factors hold pLDDT scores. Mol* assumes so for any model not marked experimental, but most
878
+ * PDB entries lack that mark, so an entry with experimental methods recorded does not count.
879
+ */
880
+ function isPredictedModel(model) {
881
+ if (QualityAssessment.isApplicable(model, "pLDDT"))
882
+ return true;
883
+ if (!MmcifFormat.is(model.sourceData) || !model.atomicConformation.B_iso_or_equiv.isDefined)
884
+ return false;
885
+ if (Model.isExperimental(model))
886
+ return false;
887
+ return model.sourceData.data.db.exptl.method.rowCount === 0;
888
+ }
844
889
  const FOCUS_TAGS = {
845
890
  [StructureFocusRepresentationTags.TargetSel]: "target",
846
891
  [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.2",
4
4
  "description": "UI-less Mol* controller that exposes the viewer as objects and display operations.",
5
5
  "license": "MIT",
6
6
  "keywords": [