Bläddra i källkod

improved handling of Structure.Loci, added whole structure support to Stats

Alexander Rose 5 år sedan
förälder
incheckning
e298499326

+ 2 - 0
src/mol-model/loci.ts

@@ -96,6 +96,8 @@ namespace Loci {
         if (data instanceof Structure) {
             if (StructureElement.Loci.is(loci)) {
                 loci = StructureElement.Loci.remap(loci, data)
+            } else if (Structure.isLoci(loci)) {
+                loci = Structure.remapLoci(loci, data)
             } else if (Link.isLoci(loci)) {
                 loci = Link.remapLoci(loci, data)
             }

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

@@ -23,7 +23,7 @@ import { PrincipalAxes } from '../../../../mol-math/linear-algebra/matrix/princi
 import Matrix from '../../../../mol-math/linear-algebra/matrix/matrix';
 import { NumberArray } from '../../../../mol-util/type-helpers';
 
-/** Represents multiple element index locations */
+/** Represents multiple structure element index locations */
 export interface Loci {
     readonly kind: 'element-loci',
     readonly structure: Structure,

+ 21 - 1
src/mol-model/structure/structure/element/stats.ts

@@ -17,12 +17,14 @@ export interface Stats {
     residueCount: number
     chainCount: number
     unitCount: number
+    structureCount: number
 
     firstElementLoc: Location
     firstConformationLoc: Location
     firstResidueLoc: Location
     firstChainLoc: Location
     firstUnitLoc: Location
+    firstStructureLoc: Location
 }
 
 export namespace Stats {
@@ -33,12 +35,14 @@ export namespace Stats {
             residueCount: 0,
             chainCount: 0,
             unitCount: 0,
+            structureCount: 0,
 
             firstElementLoc: Location.create(),
             firstConformationLoc: Location.create(),
             firstResidueLoc: Location.create(),
             firstChainLoc: Location.create(),
             firstUnitLoc: Location.create(),
+            firstStructureLoc: Location.create(),
         }
     }
 
@@ -277,8 +281,16 @@ export namespace Stats {
 
     export function ofLoci(loci: Loci) {
         const stats = create()
+        if (Loci.isEmpty(loci)) return stats
+
         let hasPartitions = false;
-        if (!Loci.isEmpty(loci)) {
+        if (Loci.isWholeStructure(loci)) {
+            stats.structureCount += 1
+            if (stats.structureCount === 1) {
+                const { unit, indices } = loci.elements[0]
+                Location.set(stats.firstStructureLoc, unit, unit.elements[OrderedSet.min(indices)])
+            }
+        } else {
             for (const e of loci.elements) {
                 handleElement(stats, e)
                 if (!Unit.Traits.is(e.unit.traits, Unit.Trait.Patitioned)) {
@@ -307,6 +319,7 @@ export namespace Stats {
                 }
             }
         }
+
         return stats
     }
 
@@ -342,11 +355,18 @@ export namespace Stats {
             Location.copy(out.firstUnitLoc, b.firstUnitLoc)
         }
 
+        if (a.structureCount === 1 && b.structureCount === 0) {
+            Location.copy(out.firstStructureLoc, a.firstStructureLoc)
+        } else if (a.structureCount === 0 && b.structureCount === 1) {
+            Location.copy(out.firstStructureLoc, b.firstStructureLoc)
+        }
+
         out.elementCount = a.elementCount + b.elementCount
         out.conformationCount = a.conformationCount + b.conformationCount
         out.residueCount = a.residueCount + b.residueCount
         out.chainCount = a.chainCount + b.chainCount
         out.unitCount = a.unitCount + b.unitCount
+        out.structureCount = a.structureCount + b.structureCount
         return out
     }
 }

+ 5 - 0
src/mol-model/structure/structure/structure.ts

@@ -525,6 +525,11 @@ namespace Structure {
         return loci.structure.isEmpty
     }
 
+    export function remapLoci(loci: Loci, structure: Structure) {
+        if (structure === loci.structure) return loci
+        return Loci(structure)
+    }
+
     export function create(units: ReadonlyArray<Unit>, props?: Props): Structure {
         return new Structure(units, props);
     }

+ 3 - 3
src/mol-plugin/util/interactivity.ts

@@ -213,9 +213,9 @@ namespace Interactivity {
         protected mark(current: Loci<ModelLoci>, action: MarkerAction.Select | MarkerAction.Deselect) {
             const { loci } = current
             if (StructureElement.Loci.is(loci)) {
-                // do a full deselect/select so visuals that are marked with
-                // granularity unequal to 'element' are handled properly
-                super.mark({ loci: EveryLoci }, MarkerAction.Deselect)
+                // do a full deselect/select for the current structure so visuals
+                // that are marked with granularity unequal to 'element' are handled properly
+                super.mark({ loci: Structure.Loci(loci.structure) }, MarkerAction.Deselect)
                 super.mark({ loci: this.sel.get(loci.structure) }, MarkerAction.Select)
             } else {
                 super.mark(current, action)

+ 2 - 4
src/mol-repr/structure/complex-representation.ts

@@ -57,14 +57,12 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
 
     function mark(loci: Loci, action: MarkerAction) {
         if (!_structure) return false
-        if (StructureElement.Loci.is(loci) || Link.isLoci(loci)) {
+        if (Structure.isLoci(loci) || StructureElement.Loci.is(loci) || Link.isLoci(loci)) {
             if (!Structure.areRootsEquivalent(loci.structure, _structure)) return false
             // Remap `loci` from equivalent structure to the current `_structure`
             loci = Loci.remap(loci, _structure)
             if (Loci.isEmpty(loci)) return false
-        } else if (isEveryLoci(loci)) {
-            // pass through
-        } else {
+        } else if (!isEveryLoci(loci)) {
             return false
         }
         return visual ? visual.mark(loci, action) : false

+ 2 - 4
src/mol-repr/structure/units-representation.ts

@@ -166,14 +166,12 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R
     function mark(loci: Loci, action: MarkerAction) {
         let changed = false
         if (!_structure) return false
-        if (StructureElement.Loci.is(loci) || Link.isLoci(loci)) {
+        if (Structure.isLoci(loci) || StructureElement.Loci.is(loci) || Link.isLoci(loci)) {
             if (!Structure.areRootsEquivalent(loci.structure, _structure)) return false
             // Remap `loci` from equivalent structure to the current `_structure`
             loci = Loci.remap(loci, _structure)
             if (Loci.isEmpty(loci)) return false
-        } else if (isEveryLoci(loci)) {
-            // pass through
-        } else {
+        } else if (!isEveryLoci(loci)) {
             return false
         }
         visuals.forEach(({ visual }) => {

+ 12 - 9
src/mol-theme/label.ts

@@ -52,7 +52,7 @@ function getResidueCount(unit: Unit.Atomic) {
 }
 
 export function structureElementStatsLabel(stats: StructureElement.Stats, countsOnly = false): string {
-    const { chainCount, residueCount, conformationCount, elementCount } = stats
+    const { structureCount, chainCount, residueCount, conformationCount, elementCount } = stats
 
     if (!countsOnly && elementCount === 1 && residueCount === 0 && chainCount === 0) {
         return elementLabel(stats.firstElementLoc, { granularity: 'element' })
@@ -65,6 +65,9 @@ export function structureElementStatsLabel(stats: StructureElement.Stats, counts
     } else if (!countsOnly) {
         const label: string[] = []
         let hidePrefix = false;
+        if (structureCount > 0) {
+            label.push(structureCount === 1 ? elementLabel(stats.firstStructureLoc, { granularity: 'structure' }) : otherLabel(structureCount, stats.firstStructureLoc || stats.firstElementLoc, 'structure', false))
+        }
         if (chainCount > 0) {
             label.push(chainCount === 1 ? elementLabel(stats.firstChainLoc, { granularity: 'chain' }) : otherLabel(chainCount, stats.firstChainLoc || stats.firstElementLoc, 'chain', false))
             hidePrefix = true;
@@ -83,6 +86,7 @@ export function structureElementStatsLabel(stats: StructureElement.Stats, counts
         return label.join('<small> + </small>')
     } else {
         const label: string[] = []
+        if (structureCount > 0) label.push(countLabel(structureCount, 'Structure'))
         if (chainCount > 0) label.push(countLabel(chainCount, 'Chain'))
         if (residueCount > 0) label.push(countLabel(residueCount, 'Residue'))
         if (conformationCount > 0) label.push(countLabel(conformationCount, 'Conformation'))
@@ -120,14 +124,13 @@ export function elementLabel(location: StructureElement.Location, options: Parti
 }
 
 function _elementLabel(location: StructureElement.Location, granularity: LabelGranularity = 'element', hidePrefix = false): string[] {
-    let label: string[];
-    if (hidePrefix) {
-        label = [];
-    } else {
-        const entry = `<small>${location.unit.model.entry}</small>`
-        const model = `<small>Model ${location.unit.model.modelNum}</small>`
-        const instance = `<small>Instance ${location.unit.conformation.operator.name}</small>`
-        label = [entry, model, instance]
+    const label: string[] = [];
+    if (!hidePrefix) {
+        label.push(`<small>${location.unit.model.entry}</small>`) // entry
+        if (granularity !== 'structure') {
+            label.push(`<small>Model ${location.unit.model.modelNum}</small>`) // model
+            label.push(`<small>Instance ${location.unit.conformation.operator.name}</small>`) // instance
+        }
     }
 
     if (Unit.isAtomic(location.unit)) {