Browse Source

added and use cross-link loci/location

Alexander Rose 5 years ago
parent
commit
0daf431d68

+ 5 - 14
src/mol-model-props/integrative/cross-link-restraint/color.ts

@@ -4,10 +4,8 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Bond } from '../../../mol-model/structure';
 import { Color, ColorScale } from '../../../mol-util/color';
 import { Location } from '../../../mol-model/location';
-import { Vec3 } from '../../../mol-math/linear-algebra';
 import { ParamDefinition as PD } from '../../../mol-util/param-definition'
 import { ThemeDataContext } from '../../../mol-theme/theme';
 import { ColorListName, ColorListOptionsScale } from '../../../mol-util/color/lists';
@@ -19,7 +17,7 @@ const DefaultColor = Color(0xCCCCCC)
 const Description = 'Colors cross-links by the deviation of the observed distance versus the modeled distance (e.g. modeled / `ihm_cross_link_restraint.distance_threshold`).'
 
 export const CrossLinkColorThemeParams = {
-    domain: PD.Interval([0.5, 2], { step: 0.01 }),
+    domain: PD.Interval([0.5, 1.5], { step: 0.01 }),
     list: PD.ColorList<ColorListName>('red-grey', ColorListOptionsScale),
 }
 export type CrossLinkColorThemeParams = typeof CrossLinkColorThemeParams
@@ -27,13 +25,6 @@ export function getCrossLinkColorThemeParams(ctx: ThemeDataContext) {
     return CrossLinkColorThemeParams // TODO return copy
 }
 
-const distVecA = Vec3.zero(), distVecB = Vec3.zero()
-function linkDistance(link: Bond.Location) {
-    link.aUnit.conformation.position(link.aUnit.elements[link.aIndex], distVecA)
-    link.bUnit.conformation.position(link.bUnit.elements[link.bIndex], distVecB)
-    return Vec3.distance(distVecA, distVecB)
-}
-
 export function CrossLinkColorTheme(ctx: ThemeDataContext, props: PD.Values<CrossLinkColorThemeParams>): ColorTheme<CrossLinkColorThemeParams> {
     let color: LocationColor
     let scale: ColorScale | undefined = undefined
@@ -48,10 +39,10 @@ export function CrossLinkColorTheme(ctx: ThemeDataContext, props: PD.Values<Cros
         const scaleColor = scale.color
 
         color = (location: Location): Color => {
-            if (Bond.isLocation(location)) {
-                const pairs = crossLinkRestraints.getPairs(location.aIndex, location.aUnit, location.bIndex, location.bUnit)
-                if (pairs) {
-                    return scaleColor(linkDistance(location) / pairs[0].distanceThreshold)
+            if (CrossLinkRestraint.isLocation(location)) {
+                const pair = crossLinkRestraints.pairs[location.element]
+                if (pair) {
+                    return scaleColor(CrossLinkRestraint.distance(pair) / pair.distanceThreshold)
                 }
             }
             return DefaultColor

+ 74 - 2
src/mol-model-props/integrative/cross-link-restraint/property.ts

@@ -5,10 +5,16 @@
  */
 
 import { ModelCrossLinkRestraint } from './format';
-import { Unit, StructureElement, Structure, CustomPropertyDescriptor} from '../../../mol-model/structure';
+import { Unit, StructureElement, Structure, CustomPropertyDescriptor, Bond} from '../../../mol-model/structure';
 import { PairRestraints, PairRestraint } from '../pair-restraints';
 import { CustomStructureProperty } from '../../common/custom-structure-property';
 import { CustomProperty } from '../../common/custom-property';
+import { DataLocation } from '../../../mol-model/location';
+import { DataLoci } from '../../../mol-model/loci';
+import { Sphere3D } from '../../../mol-math/geometry';
+import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
+import { bondLabel } from '../../../mol-theme/label';
+import { Vec3 } from '../../../mol-math/linear-algebra';
 
 export type CrossLinkRestraintValue = PairRestraints<CrossLinkRestraint>
 
@@ -45,8 +51,74 @@ namespace CrossLinkRestraint {
     export function isApplicable(structure: Structure) {
         return structure.models.some(m => !!ModelCrossLinkRestraint.Provider.get(m))
     }
-}
 
+    const distVecA = Vec3(), distVecB = Vec3()
+    export function distance(pair: CrossLinkRestraint) {
+        pair.unitA.conformation.position(pair.unitA.elements[pair.indexA], distVecA)
+        pair.unitB.conformation.position(pair.unitB.elements[pair.indexB], distVecB)
+        return Vec3.distance(distVecA, distVecB)
+    }
+
+    type StructureCrossLinkRestraints = { readonly structure: Structure, readonly crossLinkRestraints: CrossLinkRestraintValue }
+
+    export type Element = number
+    export interface Location extends DataLocation<StructureCrossLinkRestraints, Element> {}
+
+    export function Location(crossLinkRestraints: CrossLinkRestraintValue, structure: Structure, index?: number): Location {
+        return DataLocation('cross-link-restraints', { structure, crossLinkRestraints }, index as any);
+    }
+
+    export function isLocation(x: any): x is Location {
+        return !!x && x.kind === 'data-location' && x.tag === 'cross-link-restraints';
+    }
+
+    export function areLocationsEqual(locA: Location, locB: Location) {
+        return (
+            locA.data.structure === locB.data.structure &&
+            locA.data.crossLinkRestraints === locB.data.crossLinkRestraints &&
+            locA.element === locB.element
+        )
+    }
+
+    function _label(crossLinkRestraints: CrossLinkRestraintValue, element: Element): string {
+        const p = crossLinkRestraints.pairs[element]
+        return `Cross Link Restraint | Type: ${p.restraintType} | Threshold: ${p.distanceThreshold} \u212B | Psi: ${p.psi} | Sigma 1: ${p.sigma1} | Sigma 2: ${p.sigma2} | Distance: ${distance(p).toFixed(2)} \u212B`
+    }
+
+    export function locationLabel(location: Location): string {
+        return _label(location.data.crossLinkRestraints, location.element)
+    }
+
+    export interface Loci extends DataLoci<StructureCrossLinkRestraints, Element> { }
+
+    export function Loci(structure: Structure, crossLinkRestraints: CrossLinkRestraintValue, elements: ReadonlyArray<Element>): Loci {
+        return DataLoci('cross-link-restraints', { structure, crossLinkRestraints }, elements,
+            (boundingSphere) => getBoundingSphere(crossLinkRestraints, elements, boundingSphere),
+            () => getLabel(structure, crossLinkRestraints, elements));
+    }
+
+    export function isLoci(x: any): x is Loci {
+        return !!x && x.kind === 'data-loci' && x.tag === 'interactions';
+    }
+
+    export function getBoundingSphere(crossLinkRestraints: CrossLinkRestraintValue, elements: ReadonlyArray<Element>, boundingSphere: Sphere3D) {
+        return CentroidHelper.fromPairProvider(elements.length, (i, pA, pB) => {
+            const p = crossLinkRestraints.pairs[elements[i]]
+            p.unitA.conformation.position(p.unitA.elements[p.indexA], pA)
+            p.unitB.conformation.position(p.unitB.elements[p.indexB], pB)
+        }, boundingSphere)
+    }
+
+    export function getLabel(structure: Structure, crossLinkRestraints: CrossLinkRestraintValue, elements: ReadonlyArray<Element>) {
+        const element = elements[0]
+        if (element === undefined) return ''
+        const p = crossLinkRestraints.pairs[element]
+        return [
+            _label(crossLinkRestraints, element),
+            bondLabel(Bond.Location(structure, p.unitA, p.indexA, structure, p.unitB, p.indexB))
+        ].join('</br>')
+    }
+}
 
 //
 

+ 18 - 28
src/mol-model-props/integrative/cross-link-restraint/representation.ts

@@ -14,7 +14,7 @@ import { PickingId } from '../../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../../mol-model/loci';
 import { Interval } from '../../../mol-data/int';
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
-import { Structure, StructureElement, Bond } from '../../../mol-model/structure';
+import { Structure, StructureElement } from '../../../mol-model/structure';
 import { VisualContext } from '../../../mol-repr/visual';
 import { createLinkCylinderMesh, LinkCylinderParams } from '../../../mol-repr/structure/visual/util/link';
 import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../../../mol-repr/structure/complex-visual';
@@ -54,7 +54,7 @@ function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Str
 export const CrossLinkRestraintCylinderParams = {
     ...ComplexMeshParams,
     ...LinkCylinderParams,
-    sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
+    sizeFactor: PD.Numeric(0.5, { min: 0, max: 10, step: 0.1 }),
 }
 export type CrossLinkRestraintCylinderParams = typeof CrossLinkRestraintCylinderParams
 
@@ -62,7 +62,7 @@ export function CrossLinkRestraintVisual(materialId: number): ComplexVisual<Cros
     return ComplexMeshVisual<CrossLinkRestraintCylinderParams>({
         defaultProps: PD.getDefaultValues(CrossLinkRestraintCylinderParams),
         createGeometry: createCrossLinkRestraintCylinderMesh,
-        createLocationIterator: CrossLinkRestraintIterator,
+        createLocationIterator: createCrossLinkRestraintIterator,
         getLoci: getLinkLoci,
         eachLocation: eachCrossLink,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CrossLinkRestraintCylinderParams>, currentProps: PD.Values<CrossLinkRestraintCylinderParams>) => {
@@ -75,19 +75,14 @@ export function CrossLinkRestraintVisual(materialId: number): ComplexVisual<Cros
     }, materialId)
 }
 
-function CrossLinkRestraintIterator(structure: Structure): LocationIterator {
-    const { pairs } = CrossLinkRestraintProvider.get(structure).value!
+function createCrossLinkRestraintIterator(structure: Structure): LocationIterator {
+    const crossLinkRestraints = CrossLinkRestraintProvider.get(structure).value!
+    const { pairs } = crossLinkRestraints
     const groupCount = pairs.length
     const instanceCount = 1
-    const location = Bond.Location()
+    const location = CrossLinkRestraint.Location(crossLinkRestraints, structure)
     const getLocation = (groupIndex: number) => {
-        const pair = pairs[groupIndex]
-        location.aStructure = structure
-        location.aUnit = pair.unitA
-        location.aIndex = pair.indexA
-        location.bStructure = structure
-        location.bUnit = pair.unitB
-        location.bIndex = pair.indexB
+        location.element = groupIndex
         return location
     }
     return LocationIterator(groupCount, instanceCount, getLocation, true)
@@ -96,29 +91,24 @@ function CrossLinkRestraintIterator(structure: Structure): LocationIterator {
 function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
     const { objectId, groupId } = pickingId
     if (id === objectId) {
-        const pair = CrossLinkRestraintProvider.get(structure).value!.pairs[groupId]
+        const crossLinkRestraints = CrossLinkRestraintProvider.get(structure).value!
+        const pair = crossLinkRestraints.pairs[groupId]
         if (pair) {
-            return Bond.Loci(structure, [
-                Bond.Location(structure, pair.unitA, pair.indexA, structure, pair.unitB, pair.indexB),
-                Bond.Location(structure, pair.unitB, pair.indexB, structure, pair.unitA, pair.indexA)
-            ])
+            return CrossLinkRestraint.Loci(structure, crossLinkRestraints, [groupId])
         }
     }
     return EmptyLoci
 }
 
 function eachCrossLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
-    const crossLinks = CrossLinkRestraintProvider.get(structure).value!
     let changed = false
-    if (Bond.isLoci(loci)) {
-        if (!Structure.areEquivalent(loci.structure, structure)) return false
-        for (const b of loci.bonds) {
-            const indices = crossLinks.getPairIndices(b.aIndex, b.aUnit, b.bIndex, b.bUnit)
-            if (indices) {
-                for (let i = 0, il = indices.length; i < il; ++i) {
-                    if (apply(Interval.ofSingleton(indices[i]))) changed = true
-                }
-            }
+    if (CrossLinkRestraint.isLoci(loci)) {
+        if (!Structure.areEquivalent(loci.data.structure, structure)) return false
+        const crossLinkRestraints = CrossLinkRestraintProvider.get(structure).value!
+        if (loci.data.crossLinkRestraints !== crossLinkRestraints) return false
+
+        for (const e of loci.elements) {
+            if (apply(Interval.ofSingleton(e))) changed = true
         }
     }
     return changed

+ 3 - 4
src/mol-model-props/integrative/pair-restraints.ts

@@ -26,13 +26,12 @@ export class PairRestraints<T extends PairRestraint> {
     /** Indices into this.pairs */
     getPairIndices(indexA: StructureElement.UnitIndex, unitA: Unit, indexB: StructureElement.UnitIndex, unitB: Unit): ReadonlyArray<number> {
         const key = getPairKey(indexA, unitA, indexB, unitB)
-        const indices = this.pairKeyIndices.get(key)
-        return indices !== undefined ? indices : emptyArray
+        return this.pairKeyIndices.get(key) || emptyArray
     }
 
-    getPairs(indexA: StructureElement.UnitIndex, unitA: Unit, indexB: StructureElement.UnitIndex, unitB: Unit): T[] | undefined {
+    getPairs(indexA: StructureElement.UnitIndex, unitA: Unit, indexB: StructureElement.UnitIndex, unitB: Unit): T[] {
         const indices = this.getPairIndices(indexA, unitA, indexB, unitB)
-        return indices.length ? indices.map(idx => this.pairs[idx]) : undefined
+        return indices.map(idx => this.pairs[idx])
     }
 
     constructor(public pairs: ReadonlyArray<T>) {