diff --git a/src/mol-geo/representation/index.ts b/src/mol-geo/representation/index.ts index 3bb9db0d29abeaa5fca0a07c80490c0fd0da4b6f..ed37c20b33151a6e943fd15269a96798c8fff7bf 100644 --- a/src/mol-geo/representation/index.ts +++ b/src/mol-geo/representation/index.ts @@ -5,8 +5,9 @@ */ import { Task } from 'mol-task' -import { RenderObject } from 'mol-gl/render-object'; -import { PickingId, PickingInfo } from '../util/picking'; +import { RenderObject } from 'mol-gl/render-object' +import { PickingId } from '../util/picking'; +import { Loci } from 'mol-model/loci'; export interface RepresentationProps {} @@ -14,5 +15,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 + getLoci: (pickingId: PickingId) => Loci | null } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/bond.ts b/src/mol-geo/representation/structure/bond.ts index 06addb9a00ac0da36480f4146ef787869f28f7c5..ee73d7bd93cf3b5bd49d7b0ec6e5e57cd907d58d 100644 --- a/src/mol-geo/representation/structure/bond.ts +++ b/src/mol-geo/representation/structure/bond.ts @@ -21,6 +21,7 @@ import { MeshBuilder } from '../../shape/mesh-builder'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { createUniformColor } from '../../util/color-data'; import { defaults } from 'mol-util'; +import { SortedArray } from 'mol-data/int'; function createBondMesh(unit: Unit, mesh?: Mesh) { return Task.create('Cylinder mesh', async ctx => { @@ -31,7 +32,7 @@ function createBondMesh(unit: Unit, mesh?: Mesh) { if (!count) return Mesh.createEmpty(mesh) - // TODO calculate properly + // TODO calculate vertextCount properly const vertexCount = 32 * count const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh) @@ -91,7 +92,7 @@ export default function Bond(): UnitsRepresentation<BondProps> { let cylinders: MeshRenderObject let currentProps: typeof DefaultBondProps let mesh: Mesh - // let currentGroup: Unit.SymmetryGroup + let currentGroup: Unit.SymmetryGroup // let vertexMap: VertexMap return { @@ -101,7 +102,7 @@ export default function Bond(): UnitsRepresentation<BondProps> { return Task.create('Bond.create', async ctx => { renderObjects.length = 0 // clear - // currentGroup = group + currentGroup = group mesh = await createBondMesh(group.units[0]).runAsChild(ctx, 'Computing bond mesh') // console.log(mesh) @@ -165,13 +166,12 @@ export default function Bond(): UnitsRepresentation<BondProps> { return true }) }, - getLocation(pickingId: PickingId) { + getLoci(pickingId: PickingId) { // const { objectId, instanceId, elementId } = pickingId // if (cylinders.id === objectId) { - // const l = Element.Location() - // l.unit = currentGroup.units[instanceId] - // l.element = currentGroup.elements[elementId] - // return l + // const unit = currentGroup.units[instanceId] + // const elements = SortedArray.ofSingleton(currentGroup.elements[elementId]) + // return Element.Loci([{ unit, elements }]) // } return null } diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index fdddec8de9566704904b87b7a04f1a4b60b0ce48..6512d2f677841370afeaa71e4e2545f9f135b3d8 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -5,26 +5,26 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Structure, StructureSymmetry, Unit, Element, Queries } from 'mol-model/structure'; +import { Structure, StructureSymmetry, Unit } 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'; +import { PickingId } from '../../util/picking'; +import { Loci } from 'mol-model/loci'; 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 + getLoci: (pickingId: PickingId) => Loci | 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 + getLoci: (pickingId: PickingId) => Loci | null } interface GroupRepresentation<T> { @@ -32,36 +32,6 @@ interface GroupRepresentation<T> { group: Unit.SymmetryGroup } -function label(loc: Element.Location) { - const model = loc.unit.model.label - const instance = loc.unit.conformation.operator.name - 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, @@ -77,9 +47,9 @@ export function StructureRepresentation<P extends StructureProps>(reprCtor: () = const groupReprs: GroupRepresentation<P>[] = [] // let currentProps: typeof DefaultStructureProps - function getLocation(pickingId: PickingId) { + function getLoci(pickingId: PickingId) { for (let i = 0, il = groupReprs.length; i < il; ++i) { - const loc = groupReprs[i].repr.getLocation(pickingId) + const loc = groupReprs[i].repr.getLoci(pickingId) if (loc) return loc } return null @@ -121,10 +91,6 @@ export function StructureRepresentation<P extends StructureProps>(reprCtor: () = } }) }, - getLocation, - getLabel(pickingId: PickingId) { - const loc = getLocation(pickingId) - return loc ? label(loc) : null - } + getLoci } } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/point.ts b/src/mol-geo/representation/structure/point.ts index cf090acac8cafb697f1d0c96d60fe4794a843f5e..31aa640e5fc559c12cc85d80f75e97bfd583f83b 100644 --- a/src/mol-geo/representation/structure/point.ts +++ b/src/mol-geo/representation/structure/point.ts @@ -155,13 +155,12 @@ export default function Point(): UnitsRepresentation<PointProps> { return false }) }, - getLocation(pickingId: PickingId) { + getLoci(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 + const unit = currentGroup.units[instanceId] + const elements = SortedArray.ofSingleton(currentGroup.elements[elementId]) + return Element.Loci([{ unit, elements }]) } return null } diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index abc4e389cf610ea15c102a215ae7f2b2b7a52b1d..3411e43bfc4e9167304273d03199b09e33d38056 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -19,6 +19,7 @@ import { RenderableState, MeshValues } from 'mol-gl/renderable'; import { getMeshData } from '../../util/mesh-data'; import { Mesh } from '../../shape/mesh'; import { PickingId } from '../../util/picking'; +import { SortedArray } from 'mol-data/int'; function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) { let radius: Element.Property<number> @@ -150,13 +151,12 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { return true }) }, - getLocation(pickingId: PickingId) { + getLoci(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 + const unit = currentGroup.units[instanceId] + const elements = SortedArray.ofSingleton(currentGroup.elements[elementId]) + return Element.Loci([{ unit, elements }]) } return null } diff --git a/src/mol-geo/representation/volume/index.ts b/src/mol-geo/representation/volume/index.ts index f8edfdaad75d5dbce7d9e2c4fad7a793b9d02d56..b38ba274915eefd19b80d73b4cb98363f8a1b795 100644 --- a/src/mol-geo/representation/volume/index.ts +++ b/src/mol-geo/representation/volume/index.ts @@ -9,6 +9,7 @@ import { RenderObject } from 'mol-gl/render-object'; import { RepresentationProps, Representation } from '..'; import { VolumeData } from 'mol-model/volume'; import { PickingId, PickingInfo } from '../../util/picking'; +import { Loci } from 'mol-model/loci'; export interface VolumeElementRepresentation<P> { renderObjects: ReadonlyArray<RenderObject> @@ -21,7 +22,7 @@ export interface VolumeRepresentation<P extends RepresentationProps = {}> extend renderObjects: ReadonlyArray<RenderObject> create: (volumeData: VolumeData, props?: P) => Task<void> update: (props: P) => Task<void> - getLabel: (pickingId: PickingId) => PickingInfo | null + getLoci: (pickingId: PickingId) => Loci | null } export function VolumeRepresentation<P>(reprCtor: () => VolumeElementRepresentation<P>): VolumeRepresentation<P> { @@ -39,7 +40,8 @@ export function VolumeRepresentation<P>(reprCtor: () => VolumeElementRepresentat update(props: P) { return Task.create('VolumeRepresentation.update', async ctx => {}) }, - getLabel(pickingId: PickingId) { + getLoci(pickingId: PickingId) { + // TODO return null } } diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts new file mode 100644 index 0000000000000000000000000000000000000000..81fba647830710cb78043dbc1ccb6786a441a6aa --- /dev/null +++ b/src/mol-model/loci.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Element } from './structure' +import { Bond } from './structure/structure/unit/bonds' + +export type Loci = Element.Loci | Bond.Loci \ No newline at end of file diff --git a/src/mol-view/label.ts b/src/mol-view/label.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc6e7d76835653d13cc7c4822c4d82e14b5cb19f --- /dev/null +++ b/src/mol-view/label.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Unit, Element, Queries } from 'mol-model/structure'; +import { Bond } from 'mol-model/structure/structure/unit/bonds'; +import { Loci } from 'mol-model/loci'; + +export function labelFirst(loci: Loci) { + if(Element.isLoci(loci)) { + const e = loci.elements[0] + if (e && e.elements[0] !== undefined) { + return elementLabel(Element.Location(e.unit, e.elements[0])) + } + } else if (Bond.isLoci(loci)) { + const bond = loci.bonds[0] + if (bond) { + return `${elementLabel(bond.a)} - ${elementLabel(bond.b)}` + } + } + return '' +} + +export function elementLabel(loc: Element.Location) { + const model = loc.unit.model.label + const instance = loc.unit.conformation.operator.name + 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 `${model} ${instance} ${element}` +} \ No newline at end of file diff --git a/src/mol-view/viewer.ts b/src/mol-view/viewer.ts index e2a6dc57ef38e03d0279dddcebdc749afb257d0f..c0b89c6fed96494b4e9c8feed020bab948dcb4f4 100644 --- a/src/mol-view/viewer.ts +++ b/src/mol-view/viewer.ts @@ -22,6 +22,7 @@ import { createRenderTarget } from 'mol-gl/webgl/render-target'; import Scene from 'mol-gl/scene'; import { RenderVariant } from 'mol-gl/webgl/render-item'; import { PickingId, decodeIdRGBA } from 'mol-geo/util/picking'; +import { labelFirst } from './label'; interface Viewer { center: (p: Vec3) => void @@ -80,8 +81,8 @@ namespace Viewer { const p = identify(x, y) let label = '' reprMap.forEach((roSet, repr) => { - const info = repr.getLabel(p) - if (info) label = info.label + const loci = repr.getLoci(p) + if (loci) label = labelFirst(loci) repr.update({ hoverSelection: p }).run().then(() => { scene.update() requestDraw()