JonStargaryen 3 роки тому
батько
коміт
b47523372e
2 змінених файлів з 40 додано та 8 видалено
  1. 15 7
      src/viewer/helpers/preset.ts
  2. 25 1
      src/viewer/helpers/selection.ts

+ 15 - 7
src/viewer/helpers/preset.ts

@@ -28,7 +28,15 @@ import { FlexibleStructureFromModel } from './superpose/flexible-structure';
 import { PluginCommands } from 'molstar/lib/mol-plugin/commands';
 import { InteractivityManager } from 'molstar/lib/mol-plugin-state/manager/interactivity';
 import { MembraneOrientationPreset } from 'molstar/lib/extensions/anvil/behavior';
-import { createSelectionExpression, Range, SelectionExpression, Target, targetToLoci, toRange } from './selection';
+import {
+    normalizeTargets,
+    createSelectionExpressions,
+    Range,
+    SelectionExpression,
+    Target,
+    targetToLoci,
+    toRange
+} from './selection';
 import { RcsbSuperpositionRepresentationPreset } from './superpose/preset';
 
 type BaseProps = {
@@ -179,10 +187,10 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({
             let selectionExpressions: SelectionExpression[] = [];
             if (p.selection) {
                 for (const range of p.selection) {
-                    selectionExpressions = selectionExpressions.concat(createSelectionExpression(entryId, range));
+                    selectionExpressions = selectionExpressions.concat(createSelectionExpressions(entryId, range));
                 }
             } else {
-                selectionExpressions = selectionExpressions.concat(createSelectionExpression(entryId));
+                selectionExpressions = selectionExpressions.concat(createSelectionExpressions(entryId));
             }
 
             const params = {
@@ -192,12 +200,12 @@ export const RcsbPreset = TrajectoryHierarchyPresetProvider({
                 selectionExpressions: selectionExpressions
             };
             representation = await plugin.builders.structure.representation.applyPreset<any>(structureProperties!, RcsbSuperpositionRepresentationPreset, params);
-        } else if (p.kind === 'motif') {
+        } else if (p.kind === 'motif' && structure?.obj) {
             // let's force ASM_1 for motifs (as we use this contract in the rest of the stack)
             // TODO should ASM_1 be the default, seems like we'd run into problems when selecting ligands that are e.g. ambiguous with asym_id & seq_id alone?
-            const targets = p.targets.map(t => { return t.operatorName ? t : { ...t, operatorName: 'ASM_1' }; });
-            let selectionExpressions = createSelectionExpression(p.label || model.data!.entryId, targets);
-            const globalExpressions = createSelectionExpression(p.label || model.data!.entryId); // global reps, to be hidden
+            const targets = normalizeTargets(p.targets, structure!.obj.data);
+            let selectionExpressions = createSelectionExpressions(p.label || model.data!.entryId, targets);
+            const globalExpressions = createSelectionExpressions(p.label || model.data!.entryId); // global reps, to be hidden
             selectionExpressions = selectionExpressions.concat(globalExpressions.map(e => { return { ...e, isHidden: true }; }));
 
             if (p.color) {

+ 25 - 1
src/viewer/helpers/selection.ts

@@ -14,6 +14,11 @@ export type Target = {
      * Mol*-internal representation, like 'ASM_2'. Enumerated in the order of appearance in the source file.
      */
     readonly operatorName?: string
+    /**
+     * Strucmotif-/BioJava-specific representation, like 'Px42'. This is a single 'pdbx_struct_oper_list.id' value or a
+     * combination thereof.
+     */
+    readonly structOperExpression?: string
 }
 
 export type Range = {
@@ -37,12 +42,31 @@ export type SelectionExpression = {
     color?: number
 };
 
+/**
+ * This serves as adapter between the strucmotif-/BioJava-approach to identify transformed chains and the Mol* way.
+ * Looks for 'structOperExpression', converts it to an 'operatorName', and removes the original value. This will
+ * override pre-existing 'operatorName' values.
+ * @param targets collection to process
+ * @param structure parent structure
+ * @param operatorName optional value to which missing operators are set, will default to 'ASM_1' if not specified
+ */
+export function normalizeTargets(targets: Target[], structure: Structure, operatorName: string = 'ASM_1'): Target[] {
+    return targets.map(t => {
+        if (t.structOperExpression) {
+            const { structOperExpression, ...others } = t;
+            const oper = ''; // TODO impl
+            return { ...others, operatorName: oper };
+        }
+        return t.operatorName ? t : { ...t, operatorName };
+    });
+}
+
 /**
  * Convert a selection to an array of selection expressions.
  * @param labelBase the base label that will appear in the UI (e.g., the entry ID)
  * @param selection a selection by Range or a set of Targets
  */
-export function createSelectionExpression(labelBase: string, selection?: Range | Target[]): SelectionExpression[] {
+export function createSelectionExpressions(labelBase: string, selection?: Range | Target[]): SelectionExpression[] {
     if (selection) {
         if ('label_asym_id' in selection && 'label_seq_id' in selection) {
             const range = selection as Range;