Browse Source

wip, picking location and label

Alexander Rose 6 years ago
parent
commit
6056e226b6

+ 2 - 0
src/mol-geo/representation/index.ts

@@ -6,6 +6,7 @@
 
 import { Task } from 'mol-task'
 import { RenderObject } from 'mol-gl/render-object';
+import { PickingId, PickingInfo } from '../util/picking';
 
 export interface RepresentationProps {}
 
@@ -13,4 +14,5 @@ export interface Representation<D, P extends RepresentationProps = {}> {
     renderObjects: ReadonlyArray<RenderObject>
     create: (data: D, props?: P) => Task<void>
     update: (props: P) => Task<void>
+    getLabel: (pickingId: PickingId) => PickingInfo | null
 }

+ 48 - 1
src/mol-geo/representation/structure/index.ts

@@ -5,22 +5,26 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Structure, StructureSymmetry, Unit } from 'mol-model/structure';
+import { Structure, StructureSymmetry, Unit, Element, Queries } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { RenderObject } from 'mol-gl/render-object';
 import { Representation, RepresentationProps } from '..';
 import { ColorTheme } from '../../theme';
+import { PickingId, PickingInfo } from '../../util/picking';
 
 export interface UnitsRepresentation<P> {
     renderObjects: ReadonlyArray<RenderObject>
     create: (group: Unit.SymmetryGroup, props: P) => Task<void>
     update: (props: P) => Task<boolean>
+    getLocation: (pickingId: PickingId) => Element.Location | null
 }
 
 export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> {
     renderObjects: ReadonlyArray<RenderObject>
     create: (structure: Structure, props?: P) => Task<void>
     update: (props: P) => Task<void>
+    getLocation: (pickingId: PickingId) => Element.Location | null
+    getLabel: (pickingId: PickingId) => PickingInfo | null
 }
 
 interface GroupRepresentation<T> {
@@ -28,6 +32,36 @@ interface GroupRepresentation<T> {
     group: Unit.SymmetryGroup
 }
 
+function label(loc: Element.Location) {
+    const model = loc.unit.model.label
+    const instance = loc.unit.label
+    let element = ''
+
+    if (Unit.isAtomic(loc.unit)) {
+        const asym_id = Queries.props.chain.auth_asym_id(loc)
+        const seq_id = Queries.props.residue.auth_seq_id(loc)
+        const comp_id = Queries.props.residue.auth_comp_id(loc)
+        const atom_id = Queries.props.atom.auth_atom_id(loc)
+        element = `[${comp_id}]${seq_id}:${asym_id}.${atom_id}`
+    } else if (Unit.isCoarse(loc.unit)) {
+        const asym_id = Queries.props.coarse.asym_id(loc)
+        const seq_id_begin = Queries.props.coarse.seq_id_begin(loc)
+        const seq_id_end = Queries.props.coarse.seq_id_end(loc)
+        if (seq_id_begin === seq_id_end) {
+            const entityKey = Queries.props.coarse.entityKey(loc)
+            const seq = loc.unit.model.sequence.byEntityKey[entityKey]
+            const comp_id = seq.compId.value(seq_id_begin)
+            element = `[${comp_id}]${seq_id_begin}:${asym_id}`
+        } else {
+            element = `${seq_id_begin}-${seq_id_end}:${asym_id}`
+        }
+    } else {
+        element = 'unknown'
+    }
+
+    return { label: `${model} ${instance} ${element}` }
+}
+
 export const DefaultStructureProps = {
     colorTheme: { name: 'instance-index' } as ColorTheme,
     alpha: 1,
@@ -42,6 +76,14 @@ export function StructureRepresentation<P extends StructureProps>(reprCtor: () =
     const groupReprs: GroupRepresentation<P>[] = []
     // let currentProps: typeof DefaultStructureProps
 
+    function getLocation(pickingId: PickingId) {
+        for (let i = 0, il = groupReprs.length; i < il; ++i) {
+            const loc = groupReprs[i].repr.getLocation(pickingId)
+            if (loc) return loc
+        }
+        return null
+    }
+
     return {
         renderObjects,
         create(structure: Structure, props: P = {} as P) {
@@ -77,6 +119,11 @@ export function StructureRepresentation<P extends StructureProps>(reprCtor: () =
                     renderObjects.push(...repr.renderObjects)
                 }
             })
+        },
+        getLocation,
+        getLabel(pickingId: PickingId) {
+            const loc = getLocation(pickingId)
+            return loc ? label(loc) : null
         }
     }
 }

+ 24 - 10
src/mol-geo/representation/structure/point.ts

@@ -18,6 +18,7 @@ import { createTransforms, createColors, createSizes } from './utils';
 import { deepEqual, defaults } from 'mol-util';
 import { SortedArray } from 'mol-data/int';
 import { RenderableState, PointValues } from 'mol-gl/renderable';
+import { PickingId } from '../../util/picking';
 
 export const DefaultPointProps = {
     colorTheme: { name: 'instance-index' } as ColorTheme,
@@ -50,7 +51,8 @@ export function createPointVertices(unit: Unit) {
 export default function Point(): UnitsRepresentation<PointProps> {
     const renderObjects: RenderObject[] = []
     let points: PointRenderObject
-    let curProps = DefaultPointProps
+    let currentProps = DefaultPointProps
+    let currentGroup: Unit.SymmetryGroup
 
     let _units: ReadonlyArray<Unit>
     let _elements: SortedArray
@@ -58,14 +60,16 @@ export default function Point(): UnitsRepresentation<PointProps> {
     return {
         renderObjects,
         create(group: Unit.SymmetryGroup, props: PointProps = {}) {
+            currentProps = Object.assign({}, DefaultPointProps, props)
+
             return Task.create('Point.create', async ctx => {
                 renderObjects.length = 0 // clear
-                curProps = { ...DefaultPointProps, ...props }
+                currentGroup = group
 
                 _units = group.units
                 _elements = group.elements;
 
-                const { colorTheme, sizeTheme } = curProps
+                const { colorTheme, sizeTheme } = currentProps
                 const elementCount = _elements.length
 
                 const vertexMap = VertexMap.create(
@@ -119,8 +123,8 @@ export default function Point(): UnitsRepresentation<PointProps> {
             return Task.create('Point.update', async ctx => {
                 if (!points || !_units || !_elements) return false
 
-                const newProps = { ...curProps, ...props }
-                if (deepEqual(curProps, newProps)) {
+                const newProps = { ...currentProps, ...props }
+                if (deepEqual(currentProps, newProps)) {
                     console.log('props identical, nothing to change')
                     return true
                 }
@@ -135,20 +139,30 @@ export default function Point(): UnitsRepresentation<PointProps> {
                 //     fillSerial(new Uint32Array(elementCount + 1))
                 // )
 
-                if (!deepEqual(curProps.colorTheme, newProps.colorTheme)) {
-                    console.log('colorTheme changed', curProps.colorTheme, newProps.colorTheme)
+                if (!deepEqual(currentProps.colorTheme, newProps.colorTheme)) {
+                    console.log('colorTheme changed', currentProps.colorTheme, newProps.colorTheme)
                     // await ctx.update('Computing point colors');
                     // const color = createColors(_units, _elementGroup, vertexMap, newProps.colorTheme)
                     // ValueCell.update(points.props.color, color)
                 }
 
-                if (!deepEqual(curProps.sizeTheme, newProps.sizeTheme)) {
-                    console.log('sizeTheme changed', curProps.sizeTheme, newProps.sizeTheme)
+                if (!deepEqual(currentProps.sizeTheme, newProps.sizeTheme)) {
+                    console.log('sizeTheme changed', currentProps.sizeTheme, newProps.sizeTheme)
                 }
 
-                curProps = newProps
+                currentProps = newProps
                 return false
             })
+        },
+        getLocation(pickingId: PickingId) {
+            const { objectId, instanceId, elementId } = pickingId
+            if (points.id === objectId) {
+                const l = Element.Location()
+                l.unit = currentGroup.units[instanceId]
+                l.element = currentGroup.elements[elementId]
+                return l
+            }
+            return null
         }
     }
 }

+ 12 - 3
src/mol-geo/representation/structure/spacefill.ts

@@ -22,6 +22,7 @@ import { fillSerial } from 'mol-gl/renderable/util';
 import { RenderableState, MeshValues } from 'mol-gl/renderable';
 import { getMeshData } from '../../util/mesh-data';
 import { Mesh } from '../../shape/mesh';
+import { PickingId } from '../../util/picking';
 
 export const DefaultSpacefillProps = {
     ...DefaultStructureProps,
@@ -70,9 +71,7 @@ function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) {
             }
         }
 
-        const _mesh = meshBuilder.getMesh()
-        console.log(_mesh)
-        return _mesh
+        return meshBuilder.getMesh()
     })
 }
 
@@ -171,6 +170,16 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                 currentProps = newProps
                 return true
             })
+        },
+        getLocation(pickingId: PickingId) {
+            const { objectId, instanceId, elementId } = pickingId
+            if (spheres.id === objectId) {
+                const l = Element.Location()
+                l.unit = currentGroup.units[instanceId]
+                l.element = currentGroup.elements[elementId]
+                return l
+            }
+            return null
         }
     }
 }

+ 6 - 0
src/mol-geo/representation/volume/index.ts

@@ -8,17 +8,20 @@ import { Task } from 'mol-task'
 import { RenderObject } from 'mol-gl/render-object';
 import { RepresentationProps, Representation } from '..';
 import { VolumeData } from 'mol-model/volume';
+import { PickingId, PickingInfo } from '../../util/picking';
 
 export interface VolumeElementRepresentation<P> {
     renderObjects: ReadonlyArray<RenderObject>
     create: (volumeData: VolumeData, props: P) => Task<void>
     update: (props: P) => Task<boolean>
+    getLabel: (pickingId: PickingId) => PickingInfo | null
 }
 
 export interface VolumeRepresentation<P extends RepresentationProps = {}> extends Representation<VolumeData, P> {
     renderObjects: ReadonlyArray<RenderObject>
     create: (volumeData: VolumeData, props?: P) => Task<void>
     update: (props: P) => Task<void>
+    getLabel: (pickingId: PickingId) => PickingInfo | null
 }
 
 export function VolumeRepresentation<P>(reprCtor: () => VolumeElementRepresentation<P>): VolumeRepresentation<P> {
@@ -35,6 +38,9 @@ export function VolumeRepresentation<P>(reprCtor: () => VolumeElementRepresentat
         },
         update(props: P) {
             return Task.create('VolumeRepresentation.update', async ctx => {})
+        },
+        getLabel(pickingId: PickingId) {
+            return null
         }
     }
 }

+ 4 - 0
src/mol-geo/representation/volume/surface.ts

@@ -17,6 +17,7 @@ import { Mat4 } from 'mol-math/linear-algebra';
 import { createUniformColor } from '../../util/color-data';
 import { getMeshData } from '../../util/mesh-data';
 import { RenderableState, MeshValues } from 'mol-gl/renderable';
+import { PickingId } from '../../util/picking';
 
 export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue) {
     return Task.create<Mesh>('Volume Surface', async ctx => {
@@ -99,6 +100,9 @@ export default function Surface(): VolumeElementRepresentation<SurfaceProps> {
                 // TODO
                 return false
             })
+        },
+        getLabel(pickingId: PickingId) {
+            return null
         }
     }
 }

+ 0 - 2
src/mol-geo/util/color-data.ts

@@ -62,7 +62,6 @@ export function createAttributeColor(props: AttributeColorProps, colorData?: Col
         }
     }
     if (colorData) {
-        console.log('update colordata attribute')
         ValueCell.update(colorData.aColor, colors)
         if (colorData.dColorType.ref.value !== 'attribute') {
             ValueCell.update(colorData.dColorType, 'attribute')
@@ -81,7 +80,6 @@ export function createAttributeColor(props: AttributeColorProps, colorData?: Col
 
 export function createTextureColor(colors: TextureImage, type: ColorType, colorData?: ColorData): ColorData {
     if (colorData) {
-        console.log('update colordata texture')
         ValueCell.update(colorData.tColor, colors)
         ValueCell.update(colorData.uColorTexSize, Vec2.create(colors.width, colors.height))
         if (colorData.dColorType.ref.value !== type) {

+ 27 - 0
src/mol-geo/util/picking.ts

@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+function decodeFloatRGBA(r: number, g: number, b: number) {
+    r = Math.floor(r)
+    g = Math.floor(g)
+    b = Math.floor(b)
+    return r * 256 * 256 + g * 256 + b
+}
+
+export function decodeIdRGBA(r: number, g: number, b: number) {
+    return decodeFloatRGBA(r, g, b) - 1
+}
+
+export interface PickingId {
+    objectId: number
+    instanceId: number
+    elementId: number
+}
+
+export interface PickingInfo {
+    label: string
+    data?: any
+}

+ 7 - 18
src/mol-view/viewer.ts

@@ -21,23 +21,7 @@ import { Representation } from 'mol-geo/representation';
 import { createRenderTarget } from 'mol-gl/webgl/render-target';
 import Scene from 'mol-gl/scene';
 import { RenderVariant } from 'mol-gl/webgl/render-item';
-
-function decodeFloatRGBA(r: number, g: number, b: number) {
-    r = Math.floor(r)
-    g = Math.floor(g)
-    b = Math.floor(b)
-    return r * 256 * 256 + g * 256 + b
-}
-
-function decodeIdRGBA(r: number, g: number, b: number) {
-    return decodeFloatRGBA(r, g, b) - 1
-}
-
-interface PickingId {
-    objectId: number
-    instanceId: number
-    elementId: number
-}
+import { PickingId, decodeIdRGBA } from 'mol-geo/util/picking';
 
 interface Viewer {
     center: (p: Vec3) => void
@@ -94,7 +78,12 @@ namespace Viewer {
         input.resize.subscribe(handleResize)
         input.move.subscribe(({x, y}) => {
             const p = identify(x, y)
-            identified.next(`Object: ${p.objectId}, Instance: ${p.instanceId}, Element: ${p.elementId}`)
+            let label = ''
+            reprMap.forEach((roSet, repr) => {
+                const info = repr.getLabel(p)
+                if (info) label = info.label
+            })
+            identified.next(`Object: ${p.objectId}, Instance: ${p.instanceId}, Element: ${p.elementId}, Label: ${label}`)
         })
 
         const camera = PerspectiveCamera.create({