From d549b1c4c319c6d226f94dfc66c2191df6f6c669 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Thu, 7 Jun 2018 12:51:47 +0200 Subject: [PATCH] bond picking --- src/mol-geo/representation/structure/bond.ts | 125 +++++++++++------- src/mol-geo/representation/structure/point.ts | 7 +- .../representation/structure/spacefill.ts | 12 +- src/mol-geo/representation/structure/utils.ts | 4 +- src/mol-geo/util/flag-data.ts | 6 +- src/mol-math/graph/int/graph.ts | 2 +- src/mol-model/structure/structure/element.ts | 6 +- .../structure/structure/unit/bonds.ts | 20 +-- src/mol-view/label.ts | 12 +- src/mol-view/stage.ts | 9 +- 10 files changed, 122 insertions(+), 81 deletions(-) diff --git a/src/mol-geo/representation/structure/bond.ts b/src/mol-geo/representation/structure/bond.ts index 1495ab1c0..afb2cf89c 100644 --- a/src/mol-geo/representation/structure/bond.ts +++ b/src/mol-geo/representation/structure/bond.ts @@ -8,7 +8,7 @@ import { ValueCell } from 'mol-util/value-cell' import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object' -import { Unit, Element } from 'mol-model/structure'; +import { Unit, Element, Bond } from 'mol-model/structure'; import { UnitsRepresentation, DefaultStructureProps } from './index'; import { Task } from 'mol-task' import { createTransforms } from './utils'; @@ -21,20 +21,21 @@ 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 { Loci } from 'mol-model/loci'; -import { FlagAction, createEmptyFlags } from '../../util/flag-data'; +import { Loci, isEveryLoci } from 'mol-model/loci'; +import { FlagAction, applyFlagAction, createFlags } from '../../util/flag-data'; function createBondMesh(unit: Unit, mesh?: Mesh) { return Task.create('Cylinder mesh', async ctx => { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) const elements = unit.elements; - const { count, offset, neighbor } = unit.bonds; - - if (!count) return Mesh.createEmpty(mesh) + const bonds = unit.bonds + const { edgeCount, a, b } = bonds + + if (!edgeCount) return Mesh.createEmpty(mesh) // TODO calculate vertextCount properly - const vertexCount = 32 * count + const vertexCount = 32 * edgeCount const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh) const va = Vec3.zero() @@ -46,34 +47,23 @@ function createBondMesh(unit: Unit, mesh?: Mesh) { const l = Element.Location() l.unit = unit - for (let j = 0; j < offset.length - 1; ++j) { - const start = offset[j] - const end = offset[j + 1] - - if (end <= start) continue - - const aI = elements[j] - va[0] = x(aI) - va[1] = y(aI) - va[2] = z(aI) - for (let _bI = start; _bI < end; ++_bI) { - const bI = elements[neighbor[_bI]] - if (bI > aI) continue - - vb[0] = x(bI) - vb[1] = y(bI) - vb[2] = z(bI) - - Vec3.scale(vt, Vec3.add(vt, va, vb), 0.5) - Vec3.makeRotation(m, Vec3.create(0, 1, 0), Vec3.sub(vb, vb, va)) - Mat4.setTranslation(m, vt) - - meshBuilder.setId(j) - meshBuilder.addCylinder(m, { radiusTop: 0.2, radiusBottom: 0.2 }) - } - - if (j % 10000 === 0 && ctx.shouldUpdate) { - await ctx.update({ message: 'Cylinder mesh', current: j, max: count }); + for (let edgeIndex = 0, _eI = edgeCount * 2; edgeIndex < _eI; ++edgeIndex) { + const aI = elements[a[edgeIndex]], bI = elements[b[edgeIndex]]; + // each edge is included twice because of the "adjacency list" structure + // keep only the 1st occurence. + if (aI >= bI) continue; + va[0] = x(aI); va[1] = y(aI); va[2] = z(aI) + vb[0] = x(bI); vb[1] = y(bI); vb[2] = z(bI) + + Vec3.scale(vt, Vec3.add(vt, va, vb), 0.5) + Vec3.makeRotation(m, Vec3.create(0, 1, 0), Vec3.sub(vb, vb, va)) + Mat4.setTranslation(m, vt) + + meshBuilder.setId(edgeIndex) + meshBuilder.addCylinder(m, { radiusTop: 0.2, radiusBottom: 0.2 }) + + if (edgeIndex % 10000 === 0 && ctx.shouldUpdate) { + await ctx.update({ message: 'Cylinder mesh', current: edgeIndex, max: edgeCount }); } } @@ -88,7 +78,7 @@ export const DefaultBondProps = { } export type BondProps = Partial<typeof DefaultBondProps> -export default function Bond(): UnitsRepresentation<BondProps> { +export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps> { const renderObjects: RenderObject[] = [] let cylinders: MeshRenderObject let currentProps: typeof DefaultBondProps @@ -105,7 +95,12 @@ export default function Bond(): UnitsRepresentation<BondProps> { renderObjects.length = 0 // clear currentGroup = group - mesh = await createBondMesh(group.units[0]).runAsChild(ctx, 'Computing bond mesh') + const unit = group.units[0] + const elementCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0 + const instanceCount = group.units.length + + mesh = await createBondMesh(unit).runAsChild(ctx, 'Computing bond mesh') + // console.log(mesh) // vertexMap = VertexMap.fromMesh(mesh) @@ -116,9 +111,7 @@ export default function Bond(): UnitsRepresentation<BondProps> { const color = createUniformColor({ value: 0xFF0000 }) await ctx.update('Computing bond flags'); - const flag = createEmptyFlags() - - const instanceCount = group.units.length + const flag = createFlags(instanceCount * elementCount) const values: MeshValues = { ...getMeshData(mesh), @@ -129,7 +122,7 @@ export default function Bond(): UnitsRepresentation<BondProps> { uAlpha: ValueCell.create(defaults(props.alpha, 1.0)), uInstanceCount: ValueCell.create(instanceCount), - uElementCount: ValueCell.create(group.elements.length), + uElementCount: ValueCell.create(elementCount), elements: mesh.indexBuffer, @@ -168,17 +161,51 @@ export default function Bond(): UnitsRepresentation<BondProps> { }) }, getLoci(pickingId: PickingId) { - // const { objectId, instanceId, elementId } = pickingId - // if (cylinders.id === objectId) { - // const unit = currentGroup.units[instanceId] - // const elements = SortedArray.ofSingleton(currentGroup.elements[elementId]) - // return Element.Loci([{ unit, elements }]) - // } + const { objectId, instanceId, elementId } = pickingId + const unit = currentGroup.units[instanceId] + if (cylinders.id === objectId && Unit.isAtomic(unit)) { + return Bond.Loci([{ + aUnit: unit, + aIndex: unit.bonds.a[elementId], + bUnit: unit, + bIndex: unit.bonds.b[elementId] + }]) + } return null }, applyFlags(loci: Loci, action: FlagAction) { - currentGroup - // TODO + const group = currentGroup + const tFlag = cylinders.values.tFlag + const unit = group.units[0] + if (!Unit.isAtomic(unit)) return + + const elementCount = unit.bonds.edgeCount * 2 + const instanceCount = group.units.length + + let changed = false + const array = tFlag.ref.value.array + if (isEveryLoci(loci)) { + applyFlagAction(array, 0, elementCount * instanceCount, action) + changed = true + } else if (Bond.isLoci(loci)) { + for (const b of loci.bonds) { + const unitIdx = Unit.findUnitById(b.aUnit.id, group.units) + if (unitIdx !== -1) { + const _idx = unit.bonds.getEdgeIndex(b.aIndex, b.bIndex) + if (_idx !== -1) { + const idx = _idx + if (applyFlagAction(array, idx, idx + 1, action) && !changed) { + changed = true + } + } + } + } + } else { + return + } + if (changed) { + ValueCell.update(tFlag, tFlag.ref.value) + } } } } diff --git a/src/mol-geo/representation/structure/point.ts b/src/mol-geo/representation/structure/point.ts index a45facb64..2daed89a6 100644 --- a/src/mol-geo/representation/structure/point.ts +++ b/src/mol-geo/representation/structure/point.ts @@ -47,7 +47,7 @@ export function createPointVertices(unit: Unit) { return vertices } -export default function Point(): UnitsRepresentation<PointProps> { +export default function PointUnitsRepresentation(): UnitsRepresentation<PointProps> { const renderObjects: RenderObject[] = [] let points: PointRenderObject let currentProps = DefaultPointProps @@ -70,6 +70,7 @@ export default function Point(): UnitsRepresentation<PointProps> { const { colorTheme, sizeTheme } = currentProps const elementCount = _elements.length + const instanceCount = group.units.length const vertexMap = VertexMap.create( elementCount, @@ -91,9 +92,7 @@ export default function Point(): UnitsRepresentation<PointProps> { const size = createSizes(group, vertexMap, sizeTheme) await ctx.update('Computing spacefill flags'); - const flag = createFlags(group) - - const instanceCount = group.units.length + const flag = createFlags(instanceCount * elementCount) const values: PointValues = { aPosition: ValueCell.create(vertices), diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index d1056a033..573c18547 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -33,7 +33,7 @@ function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) { console.warn('Unsupported unit type') return Task.constant('Empty mesh', Mesh.createEmpty(mesh)) } - return createSphereMesh(unit, (l) => radius(l) * 1.0, detail, mesh) + return createSphereMesh(unit, (l) => radius(l) * 0.3, detail, mesh) } export const DefaultSpacefillProps = { @@ -44,7 +44,7 @@ export const DefaultSpacefillProps = { } export type SpacefillProps = Partial<typeof DefaultSpacefillProps> -export default function Spacefill(): UnitsRepresentation<SpacefillProps> { +export default function SpacefillUnitsRepresentation(): UnitsRepresentation<SpacefillProps> { const renderObjects: RenderObject[] = [] let spheres: MeshRenderObject let currentProps: typeof DefaultSpacefillProps @@ -62,6 +62,8 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { currentGroup = group const { detail, colorTheme } = { ...DefaultSpacefillProps, ...props } + const instanceCount = group.units.length + const elementCount = group.elements.length mesh = await createSpacefillMesh(group.units[0], detail).runAsChild(ctx, 'Computing spacefill mesh') // console.log(mesh) @@ -74,9 +76,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { const color = createColors(group, vertexMap, colorTheme) await ctx.update('Computing spacefill flags'); - const flag = createFlags(group) - - const instanceCount = group.units.length + const flag = createFlags(instanceCount * elementCount) const values: MeshValues = { ...getMeshData(mesh), @@ -87,7 +87,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { uAlpha: ValueCell.create(defaults(props.alpha, 1.0)), uInstanceCount: ValueCell.create(instanceCount), - uElementCount: ValueCell.create(group.elements.length), + uElementCount: ValueCell.create(elementCount), elements: mesh.indexBuffer, diff --git a/src/mol-geo/representation/structure/utils.ts b/src/mol-geo/representation/structure/utils.ts index a0a17964c..e6719eaa7 100644 --- a/src/mol-geo/representation/structure/utils.ts +++ b/src/mol-geo/representation/structure/utils.ts @@ -73,9 +73,7 @@ export function createSphereMesh(unit: Unit, radius: Element.Property<number>, d for (let i = 0; i < elementCount; i++) { l.element = elements[i] - v[0] = x(l.element) - v[1] = y(l.element) - v[2] = z(l.element) + v[0] = x(l.element); v[1] = y(l.element); v[2] = z(l.element) Mat4.setTranslation(m, v) meshBuilder.setId(i) diff --git a/src/mol-geo/util/flag-data.ts b/src/mol-geo/util/flag-data.ts index 206f1aeae..e8049a53e 100644 --- a/src/mol-geo/util/flag-data.ts +++ b/src/mol-geo/util/flag-data.ts @@ -4,7 +4,6 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Unit } from 'mol-model/structure' import { ValueCell } from 'mol-util/value-cell' import { Vec2 } from 'mol-math/linear-algebra' import { TextureImage, createTextureImage } from 'mol-gl/renderable/util'; @@ -72,10 +71,7 @@ export function applyFlagAction(array: Uint8Array, start: number, end: number, a return changed } -export function createFlags(group: Unit.SymmetryGroup, flagData?: FlagData): FlagData { - const instanceCount = group.units.length - const elementCount = group.elements.length - const count = instanceCount * elementCount +export function createFlags(count: number, flagData?: FlagData): FlagData { const flags = flagData && flagData.tFlag.ref.value.array.length >= count ? flagData.tFlag.ref.value : createTextureImage(count, 1) diff --git a/src/mol-math/graph/int/graph.ts b/src/mol-math/graph/int/graph.ts index fb50ee13f..bc2666a2c 100644 --- a/src/mol-math/graph/int/graph.ts +++ b/src/mol-math/graph/int/graph.ts @@ -86,7 +86,7 @@ namespace IntGraph { * builder.addNextEdge(); * builder.assignProperty(property, srcProp[i]); * } - * return builder.createGraph({ property }); + * return builder.createGraph({ property }); */ addNextEdge() { const a = this.xs[this.current], b = this.ys[this.current]; diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts index c9645caa8..af6a4cdb2 100644 --- a/src/mol-model/structure/structure/element.ts +++ b/src/mol-model/structure/structure/element.ts @@ -23,7 +23,11 @@ namespace Element { export function createEmptyArray(n: number): Element[] { return new Float64Array(n) as any; } /** All the information required to access element properties */ - export interface Location { unit: Unit, element: number } + export interface Location { + unit: Unit, + /** Index into element (atomic/coarse) properties of unit.model */ + element: number + } export function Location(unit?: Unit, element?: number): Location { return { unit: unit as any, element: element || 0 }; } export interface Property<T> { (location: Location): T } export interface Predicate extends Property<boolean> { } diff --git a/src/mol-model/structure/structure/unit/bonds.ts b/src/mol-model/structure/structure/unit/bonds.ts index 73b6ff8d6..d72687428 100644 --- a/src/mol-model/structure/structure/unit/bonds.ts +++ b/src/mol-model/structure/structure/unit/bonds.ts @@ -4,23 +4,27 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Element } from '../../structure' +import { Unit } from '../../structure' export * from './bonds/intra-data' export * from './bonds/intra-compute' -interface Bond { - readonly a: Readonly<Element.Location>, - readonly b: Readonly<Element.Location> -} - namespace Bond { + export interface Location { + readonly aUnit: Unit, + /** Index into aUnit.elements */ + readonly aIndex: number, + readonly bUnit: Unit, + /** Index into bUnit.elements */ + readonly bIndex: number, + } + export interface Loci { readonly kind: 'bond-loci', - readonly bonds: ReadonlyArray<Bond> + readonly bonds: ReadonlyArray<Location> } - export function Loci(bonds: ArrayLike<Bond>): Loci { + export function Loci(bonds: ArrayLike<Location>): Loci { return { kind: 'bond-loci', bonds: bonds as Loci['bonds'] }; } diff --git a/src/mol-view/label.ts b/src/mol-view/label.ts index 311abab26..578a4a7a5 100644 --- a/src/mol-view/label.ts +++ b/src/mol-view/label.ts @@ -9,6 +9,14 @@ import { Unit, Element, Queries } from 'mol-model/structure'; import { Bond } from 'mol-model/structure/structure/unit/bonds'; import { Loci } from 'mol-model/loci'; +const elementLocA = Element.Location() +const elementLocB = Element.Location() + +function setElementLocation(loc: Element.Location, unit: Unit, index: number) { + loc.unit = unit + loc.element = unit.elements[index] +} + export function labelFirst(loci: Loci) { if(Element.isLoci(loci)) { const e = loci.elements[0] @@ -18,7 +26,9 @@ export function labelFirst(loci: Loci) { } else if (Bond.isLoci(loci)) { const bond = loci.bonds[0] if (bond) { - return `${elementLabel(bond.a)} - ${elementLabel(bond.b)}` + setElementLocation(elementLocA, bond.aUnit, bond.aIndex) + setElementLocation(elementLocB, bond.bUnit, bond.bIndex) + return `${elementLabel(elementLocA)} - ${elementLabel(elementLocB)}` } } return '' diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts index 1992d98de..af44bda4b 100644 --- a/src/mol-view/stage.ts +++ b/src/mol-view/stage.ts @@ -7,7 +7,7 @@ import Viewer from 'mol-view/viewer' import { StateContext } from './state/context'; import { Progress } from 'mol-task'; -import { MmcifUrlToSpacefill } from './state/transform'; +import { MmcifUrlToModel, ModelToStructure, StructureToSpacefill, StructureToBond } from './state/transform'; import { UrlEntity } from './state/entity'; import { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; @@ -42,9 +42,12 @@ export class Stage { this.loadMmcifUrl(`../../examples/1cbs_full.bcif`) } - loadMmcifUrl (url: string) { + async loadMmcifUrl (url: string) { const urlEntity = UrlEntity.ofUrl(this.ctx, url) - MmcifUrlToSpacefill.apply(this.ctx, urlEntity, spacefillProps) + const modelEntity = await MmcifUrlToModel.apply(this.ctx, urlEntity) + const structureEntity = await ModelToStructure.apply(this.ctx, modelEntity) + StructureToSpacefill.apply(this.ctx, structureEntity, spacefillProps) + StructureToBond.apply(this.ctx, structureEntity, spacefillProps) // TODO props } loadPdbid (pdbid: string) { -- GitLab