Browse Source

wip, assembly symmetry getLoci/mark

Alexander Rose 6 years ago
parent
commit
bf5908e291

+ 5 - 0
src/mol-model-props/rcsb/assembly-symmetry.ts

@@ -154,6 +154,7 @@ const _Descriptor: ModelPropertyDescriptor = {
 }
 
 export interface AssemblySymmetry {
+    '@type': 'rcsb_assembly_symmetry',
     db: AssemblySymmetry.Database
     getSymmetries(assemblyId: string): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry']>
     getClusters(symmetryId: number): Table<AssemblySymmetry.Schema['rcsb_assembly_symmetry_cluster']>
@@ -168,6 +169,7 @@ export function AssemblySymmetry(db: AssemblySymmetry.Database): AssemblySymmetr
     const a = db.rcsb_assembly_symmetry_axis
 
     return {
+        '@type': 'rcsb_assembly_symmetry',
         db,
         getSymmetries: (assemblyId: string) => Table.pick(f, f._schema, i => f.assembly_id.value(i) === assemblyId),
         getClusters: (symmetryId: number) => Table.pick(c, c._schema, i => c.symmetry_id.value(i) === symmetryId),
@@ -179,6 +181,9 @@ export function AssemblySymmetry(db: AssemblySymmetry.Database): AssemblySymmetr
 const Client = new GraphQLClient(AssemblySymmetry.GraphQLEndpointURL, (url: string, type: 'string' | 'binary', body?: string) => ajaxGet({ url, type, body }) )
 
 export namespace AssemblySymmetry {
+    export function is(x: any): x is AssemblySymmetry {
+        return x['@type'] === 'rcsb_assembly_symmetry'
+    }
     export const GraphQLEndpointURL = 'http://rest-experimental.rcsb.org/graphql'
     export const Schema = {
         rcsb_assembly_symmetry_info: {

+ 30 - 12
src/mol-model-props/rcsb/representations/assembly-symmetry-axes.ts

@@ -18,9 +18,11 @@ import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { VisualUpdateState } from 'mol-repr/util';
 import { ComplexMeshVisual, ComplexMeshParams } from 'mol-repr/structure/complex-visual';
 import { Mesh } from 'mol-geo/geometry/mesh/mesh';
-import { EmptyLoci } from 'mol-model/loci';
+import { EmptyLoci, createDataLoci, Loci, isDataLoci } from 'mol-model/loci';
 import { LocationIterator } from 'mol-geo/util/location-iterator';
 import { NullLocation } from 'mol-model/location';
+import { PickingId } from 'mol-geo/geometry/picking';
+import { OrderedSet, Interval } from 'mol-data/int';
 
 export const AssemblySymmetryAxesParams = {
     ...ComplexMeshParams,
@@ -75,8 +77,8 @@ export function AssemblySymmetryAxesVisual(): ComplexVisual<AssemblySymmetryAxes
         defaultProps: PD.getDefaultValues(AssemblySymmetryAxesParams),
         createGeometry: createAssemblySymmetryAxesMesh,
         createLocationIterator,
-        getLoci: () => EmptyLoci,
-        mark: () => false,
+        getLoci,
+        mark,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<AssemblySymmetryAxesParams>, currentProps: PD.Values<AssemblySymmetryAxesParams>) => {
             state.createGeometry = (
                 newProps.sizeFactor !== currentProps.sizeFactor ||
@@ -88,15 +90,29 @@ export function AssemblySymmetryAxesVisual(): ComplexVisual<AssemblySymmetryAxes
 }
 
 function createLocationIterator(structure: Structure) {
-    let groupCount = 0
-
     const assemblySymmetry = AssemblySymmetry.get(structure.models[0])
-    if (assemblySymmetry) {
-        const axis = assemblySymmetry.db.rcsb_assembly_symmetry_axis
-        groupCount = axis._rowCount
+    const groupCount = assemblySymmetry ? assemblySymmetry.db.rcsb_assembly_symmetry_axis._rowCount : 0
+    return LocationIterator(groupCount, 1, () => NullLocation)
+}
+
+function getLoci(pickingId: PickingId, structure: Structure, id: number) {
+    const { objectId, groupId } = pickingId
+    if (id === objectId) {
+        const assemblySymmetry = AssemblySymmetry.get(structure.models[0])
+        if (assemblySymmetry) {
+            return createDataLoci(assemblySymmetry, 'axes', OrderedSet.ofSingleton(groupId))
+        }
     }
+    return EmptyLoci
+}
 
-    return LocationIterator(groupCount, 1, () => NullLocation)
+function mark(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
+    let changed = false
+    if (!isDataLoci(loci) || loci.tag !== 'axes') return false
+    const assemblySymmetry = AssemblySymmetry.get(structure.models[0])
+    if (!assemblySymmetry || loci.data !== assemblySymmetry) return false
+    OrderedSet.forEach(loci.indices, v => { if (apply(Interval.ofSingleton(v))) changed = true })
+    return changed
 }
 
 export function createAssemblySymmetryAxesMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<AssemblySymmetryAxesParams>, mesh?: Mesh) {
@@ -113,16 +129,18 @@ export function createAssemblySymmetryAxesMesh(ctx: VisualContext, structure: St
     // symmetry.assembly_id not available for structure.assemblyName
     if (symmetry.assembly_id !== structure.assemblyName) return Mesh.createEmpty(mesh)
 
-    const axes = assemblySymmetry.getAxes(symmetryId)
-    if (!axes._rowCount) return Mesh.createEmpty(mesh)
-
+    const axes = assemblySymmetry.db.rcsb_assembly_symmetry_axis
     const vectorSpace = AssemblySymmetry.Schema.rcsb_assembly_symmetry_axis.start.space;
     // const colors: Color[] = []
     // const labels: string[] = []
+
     const radius = 1 * sizeFactor
     const cylinderProps = { radiusTop: radius, radiusBottom: radius }
     const builderState = MeshBuilder.createState(256, 128, mesh)
+
      for (let i = 0, il = axes._rowCount; i < il; ++i) {
+        if (axes.symmetry_id.value(i) !== symmetryId) continue
+
         const start = Tensor.toVec3(vectorSpace, axes.start.value(i))
         const end = Tensor.toVec3(vectorSpace, axes.end.value(i))
         builderState.currentGroup = i

+ 20 - 1
src/mol-model/loci.ts

@@ -27,9 +27,28 @@ export function isEmptyLoci(x: any): x is EmptyLoci {
     return !!x && x.kind === 'empty-loci';
 }
 
+export interface DataLoci {
+    readonly kind: 'data-loci',
+    readonly data: any,
+    readonly tag: string
+    readonly indices: OrderedSet<number>
+}
+export function isDataLoci(x: any): x is DataLoci {
+    return !!x && x.kind === 'data-loci';
+}
+export function areDataLociEqual(a: DataLoci, b: DataLoci) {
+    return a.data === b.data && a.tag === b.tag && OrderedSet.areEqual(a.indices, b.indices)
+}
+export function createDataLoci(data: any, tag: string, indices: OrderedSet<number>): DataLoci {
+    return { kind: 'data-loci', data, tag, indices }
+}
+
 export function areLociEqual(lociA: Loci, lociB: Loci) {
     if (isEveryLoci(lociA) && isEveryLoci(lociB)) return true
     if (isEmptyLoci(lociA) && isEmptyLoci(lociB)) return true
+    if (isDataLoci(lociA) && isDataLoci(lociB)) {
+        return areDataLociEqual(lociA, lociB)
+    }
     if (Structure.isLoci(lociA) && Structure.isLoci(lociB)) {
         return Structure.areLociEqual(lociA, lociB)
     }
@@ -48,7 +67,7 @@ export function areLociEqual(lociA: Loci, lociB: Loci) {
 
 export { Loci }
 
-type Loci = StructureElement.Loci | Structure.Loci | Link.Loci | EveryLoci | EmptyLoci | Shape.Loci
+type Loci = StructureElement.Loci | Structure.Loci | Link.Loci | EveryLoci | EmptyLoci | DataLoci | Shape.Loci
 
 namespace Loci {
 

+ 23 - 1
src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts

@@ -10,6 +10,9 @@ import { AssemblySymmetry } from 'mol-model-props/rcsb/assembly-symmetry';
 import { CustomPropertyRegistry } from 'mol-plugin/util/custom-prop-registry';
 import { AssemblySymmetryClusterColorThemeProvider } from 'mol-model-props/rcsb/themes/assembly-symmetry-cluster';
 import { AssemblySymmetryAxesRepresentationProvider } from 'mol-model-props/rcsb/representations/assembly-symmetry-axes';
+import { Loci, isDataLoci } from 'mol-model/loci';
+import { OrderedSet } from 'mol-data/int';
+import { Table } from 'mol-data/db';
 
 export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean }>({
     name: 'rcsb-assembly-symmetry-prop',
@@ -27,6 +30,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
 
         register(): void {
             this.ctx.customModelProperties.register(this.provider);
+            this.ctx.lociLabels.addProvider(labelAssemblySymmetryAxes);
 
             // TODO: support filtering of themes and representations based on the input structure
             // in this case, it would check structure.models[0].customProperties.has(AssemblySymmetry.Descriptor)
@@ -43,6 +47,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
 
         unregister() {
             this.ctx.customModelProperties.unregister(AssemblySymmetry.Descriptor.name);
+            this.ctx.lociLabels.removeProvider(labelAssemblySymmetryAxes);
             this.ctx.structureRepresentation.themeCtx.colorThemeRegistry.remove('rcsb-assembly-symmetry-cluster')
             this.ctx.structureRepresentation.registry.remove('rcsb-assembly-symmetry-axes')
         }
@@ -50,4 +55,21 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
     params: () => ({
         autoAttach: PD.Boolean(false)
     })
-});
+});
+
+function labelAssemblySymmetryAxes(loci: Loci): string | undefined {
+    if (isDataLoci(loci) && AssemblySymmetry.is(loci.data) && loci.tag === 'axes') {
+        const { rcsb_assembly_symmetry_axis: axis, rcsb_assembly_symmetry: sym } = loci.data.db
+        const labels: string[] = []
+        OrderedSet.forEach(loci.indices, v => {
+            const symmetryId = axis.symmetry_id.value(v)
+            const symmetry = Table.pickRow(sym, i => sym.id.value(i) === symmetryId)
+            if (symmetry) {
+                labels.push(`Axis of order ${axis.order.value(v)} for ${symmetry.kind} ${symmetry.type.toLowerCase()} symmetry`)
+            }
+        })
+        // labels.push(`Axis ${i + 1} for ${symmetry.kind} ${symmetry.type.toLowerCase()} symmetry`)
+        return labels.length ? labels.join(', ') : undefined
+    }
+    return undefined
+}

+ 2 - 0
src/mol-theme/label.ts

@@ -44,6 +44,8 @@ export function labelFirst(loci: Loci): string {
             return 'Everything'
         case 'empty-loci':
             return 'Nothing'
+        case 'data-loci':
+            return ''
     }
 }