Skip to content
Snippets Groups Projects
Commit d549b1c4 authored by Alexander Rose's avatar Alexander Rose
Browse files

bond picking

parent 4df30ac0
No related branches found
No related tags found
No related merge requests found
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
import { ValueCell } from 'mol-util/value-cell' import { ValueCell } from 'mol-util/value-cell'
import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object' 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 { UnitsRepresentation, DefaultStructureProps } from './index';
import { Task } from 'mol-task' import { Task } from 'mol-task'
import { createTransforms } from './utils'; import { createTransforms } from './utils';
...@@ -21,20 +21,21 @@ import { MeshBuilder } from '../../shape/mesh-builder'; ...@@ -21,20 +21,21 @@ import { MeshBuilder } from '../../shape/mesh-builder';
import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Vec3, Mat4 } from 'mol-math/linear-algebra';
import { createUniformColor } from '../../util/color-data'; import { createUniformColor } from '../../util/color-data';
import { defaults } from 'mol-util'; import { defaults } from 'mol-util';
import { Loci } from 'mol-model/loci'; import { Loci, isEveryLoci } from 'mol-model/loci';
import { FlagAction, createEmptyFlags } from '../../util/flag-data'; import { FlagAction, applyFlagAction, createFlags } from '../../util/flag-data';
function createBondMesh(unit: Unit, mesh?: Mesh) { function createBondMesh(unit: Unit, mesh?: Mesh) {
return Task.create('Cylinder mesh', async ctx => { return Task.create('Cylinder mesh', async ctx => {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
const elements = unit.elements; const elements = unit.elements;
const { count, offset, neighbor } = unit.bonds; const bonds = unit.bonds
const { edgeCount, a, b } = bonds
if (!count) return Mesh.createEmpty(mesh)
if (!edgeCount) return Mesh.createEmpty(mesh)
// TODO calculate vertextCount properly // TODO calculate vertextCount properly
const vertexCount = 32 * count const vertexCount = 32 * edgeCount
const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh) const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh)
const va = Vec3.zero() const va = Vec3.zero()
...@@ -46,34 +47,23 @@ function createBondMesh(unit: Unit, mesh?: Mesh) { ...@@ -46,34 +47,23 @@ function createBondMesh(unit: Unit, mesh?: Mesh) {
const l = Element.Location() const l = Element.Location()
l.unit = unit l.unit = unit
for (let j = 0; j < offset.length - 1; ++j) { for (let edgeIndex = 0, _eI = edgeCount * 2; edgeIndex < _eI; ++edgeIndex) {
const start = offset[j] const aI = elements[a[edgeIndex]], bI = elements[b[edgeIndex]];
const end = offset[j + 1] // each edge is included twice because of the "adjacency list" structure
// keep only the 1st occurence.
if (end <= start) continue if (aI >= bI) continue;
va[0] = x(aI); va[1] = y(aI); va[2] = z(aI)
const aI = elements[j] vb[0] = x(bI); vb[1] = y(bI); vb[2] = z(bI)
va[0] = x(aI)
va[1] = y(aI) Vec3.scale(vt, Vec3.add(vt, va, vb), 0.5)
va[2] = z(aI) Vec3.makeRotation(m, Vec3.create(0, 1, 0), Vec3.sub(vb, vb, va))
for (let _bI = start; _bI < end; ++_bI) { Mat4.setTranslation(m, vt)
const bI = elements[neighbor[_bI]]
if (bI > aI) continue meshBuilder.setId(edgeIndex)
meshBuilder.addCylinder(m, { radiusTop: 0.2, radiusBottom: 0.2 })
vb[0] = x(bI)
vb[1] = y(bI) if (edgeIndex % 10000 === 0 && ctx.shouldUpdate) {
vb[2] = z(bI) await ctx.update({ message: 'Cylinder mesh', current: edgeIndex, max: edgeCount });
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 });
} }
} }
...@@ -88,7 +78,7 @@ export const DefaultBondProps = { ...@@ -88,7 +78,7 @@ export const DefaultBondProps = {
} }
export type BondProps = Partial<typeof DefaultBondProps> export type BondProps = Partial<typeof DefaultBondProps>
export default function Bond(): UnitsRepresentation<BondProps> { export default function BondUnitsRepresentation(): UnitsRepresentation<BondProps> {
const renderObjects: RenderObject[] = [] const renderObjects: RenderObject[] = []
let cylinders: MeshRenderObject let cylinders: MeshRenderObject
let currentProps: typeof DefaultBondProps let currentProps: typeof DefaultBondProps
...@@ -105,7 +95,12 @@ export default function Bond(): UnitsRepresentation<BondProps> { ...@@ -105,7 +95,12 @@ export default function Bond(): UnitsRepresentation<BondProps> {
renderObjects.length = 0 // clear renderObjects.length = 0 // clear
currentGroup = group 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) // console.log(mesh)
// vertexMap = VertexMap.fromMesh(mesh) // vertexMap = VertexMap.fromMesh(mesh)
...@@ -116,9 +111,7 @@ export default function Bond(): UnitsRepresentation<BondProps> { ...@@ -116,9 +111,7 @@ export default function Bond(): UnitsRepresentation<BondProps> {
const color = createUniformColor({ value: 0xFF0000 }) const color = createUniformColor({ value: 0xFF0000 })
await ctx.update('Computing bond flags'); await ctx.update('Computing bond flags');
const flag = createEmptyFlags() const flag = createFlags(instanceCount * elementCount)
const instanceCount = group.units.length
const values: MeshValues = { const values: MeshValues = {
...getMeshData(mesh), ...getMeshData(mesh),
...@@ -129,7 +122,7 @@ export default function Bond(): UnitsRepresentation<BondProps> { ...@@ -129,7 +122,7 @@ export default function Bond(): UnitsRepresentation<BondProps> {
uAlpha: ValueCell.create(defaults(props.alpha, 1.0)), uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
uInstanceCount: ValueCell.create(instanceCount), uInstanceCount: ValueCell.create(instanceCount),
uElementCount: ValueCell.create(group.elements.length), uElementCount: ValueCell.create(elementCount),
elements: mesh.indexBuffer, elements: mesh.indexBuffer,
...@@ -168,17 +161,51 @@ export default function Bond(): UnitsRepresentation<BondProps> { ...@@ -168,17 +161,51 @@ export default function Bond(): UnitsRepresentation<BondProps> {
}) })
}, },
getLoci(pickingId: PickingId) { getLoci(pickingId: PickingId) {
// const { objectId, instanceId, elementId } = pickingId const { objectId, instanceId, elementId } = pickingId
// if (cylinders.id === objectId) { const unit = currentGroup.units[instanceId]
// const unit = currentGroup.units[instanceId] if (cylinders.id === objectId && Unit.isAtomic(unit)) {
// const elements = SortedArray.ofSingleton(currentGroup.elements[elementId]) return Bond.Loci([{
// return Element.Loci([{ unit, elements }]) aUnit: unit,
// } aIndex: unit.bonds.a[elementId],
bUnit: unit,
bIndex: unit.bonds.b[elementId]
}])
}
return null return null
}, },
applyFlags(loci: Loci, action: FlagAction) { applyFlags(loci: Loci, action: FlagAction) {
currentGroup const group = currentGroup
// TODO 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)
}
} }
} }
} }
...@@ -47,7 +47,7 @@ export function createPointVertices(unit: Unit) { ...@@ -47,7 +47,7 @@ export function createPointVertices(unit: Unit) {
return vertices return vertices
} }
export default function Point(): UnitsRepresentation<PointProps> { export default function PointUnitsRepresentation(): UnitsRepresentation<PointProps> {
const renderObjects: RenderObject[] = [] const renderObjects: RenderObject[] = []
let points: PointRenderObject let points: PointRenderObject
let currentProps = DefaultPointProps let currentProps = DefaultPointProps
...@@ -70,6 +70,7 @@ export default function Point(): UnitsRepresentation<PointProps> { ...@@ -70,6 +70,7 @@ export default function Point(): UnitsRepresentation<PointProps> {
const { colorTheme, sizeTheme } = currentProps const { colorTheme, sizeTheme } = currentProps
const elementCount = _elements.length const elementCount = _elements.length
const instanceCount = group.units.length
const vertexMap = VertexMap.create( const vertexMap = VertexMap.create(
elementCount, elementCount,
...@@ -91,9 +92,7 @@ export default function Point(): UnitsRepresentation<PointProps> { ...@@ -91,9 +92,7 @@ export default function Point(): UnitsRepresentation<PointProps> {
const size = createSizes(group, vertexMap, sizeTheme) const size = createSizes(group, vertexMap, sizeTheme)
await ctx.update('Computing spacefill flags'); await ctx.update('Computing spacefill flags');
const flag = createFlags(group) const flag = createFlags(instanceCount * elementCount)
const instanceCount = group.units.length
const values: PointValues = { const values: PointValues = {
aPosition: ValueCell.create(vertices), aPosition: ValueCell.create(vertices),
......
...@@ -33,7 +33,7 @@ function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) { ...@@ -33,7 +33,7 @@ function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) {
console.warn('Unsupported unit type') console.warn('Unsupported unit type')
return Task.constant('Empty mesh', Mesh.createEmpty(mesh)) 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 = { export const DefaultSpacefillProps = {
...@@ -44,7 +44,7 @@ export const DefaultSpacefillProps = { ...@@ -44,7 +44,7 @@ export const DefaultSpacefillProps = {
} }
export type SpacefillProps = Partial<typeof DefaultSpacefillProps> export type SpacefillProps = Partial<typeof DefaultSpacefillProps>
export default function Spacefill(): UnitsRepresentation<SpacefillProps> { export default function SpacefillUnitsRepresentation(): UnitsRepresentation<SpacefillProps> {
const renderObjects: RenderObject[] = [] const renderObjects: RenderObject[] = []
let spheres: MeshRenderObject let spheres: MeshRenderObject
let currentProps: typeof DefaultSpacefillProps let currentProps: typeof DefaultSpacefillProps
...@@ -62,6 +62,8 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { ...@@ -62,6 +62,8 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
currentGroup = group currentGroup = group
const { detail, colorTheme } = { ...DefaultSpacefillProps, ...props } 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') mesh = await createSpacefillMesh(group.units[0], detail).runAsChild(ctx, 'Computing spacefill mesh')
// console.log(mesh) // console.log(mesh)
...@@ -74,9 +76,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { ...@@ -74,9 +76,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
const color = createColors(group, vertexMap, colorTheme) const color = createColors(group, vertexMap, colorTheme)
await ctx.update('Computing spacefill flags'); await ctx.update('Computing spacefill flags');
const flag = createFlags(group) const flag = createFlags(instanceCount * elementCount)
const instanceCount = group.units.length
const values: MeshValues = { const values: MeshValues = {
...getMeshData(mesh), ...getMeshData(mesh),
...@@ -87,7 +87,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { ...@@ -87,7 +87,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
uAlpha: ValueCell.create(defaults(props.alpha, 1.0)), uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
uInstanceCount: ValueCell.create(instanceCount), uInstanceCount: ValueCell.create(instanceCount),
uElementCount: ValueCell.create(group.elements.length), uElementCount: ValueCell.create(elementCount),
elements: mesh.indexBuffer, elements: mesh.indexBuffer,
......
...@@ -73,9 +73,7 @@ export function createSphereMesh(unit: Unit, radius: Element.Property<number>, d ...@@ -73,9 +73,7 @@ export function createSphereMesh(unit: Unit, radius: Element.Property<number>, d
for (let i = 0; i < elementCount; i++) { for (let i = 0; i < elementCount; i++) {
l.element = elements[i] l.element = elements[i]
v[0] = x(l.element) v[0] = x(l.element); v[1] = y(l.element); v[2] = z(l.element)
v[1] = y(l.element)
v[2] = z(l.element)
Mat4.setTranslation(m, v) Mat4.setTranslation(m, v)
meshBuilder.setId(i) meshBuilder.setId(i)
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import { Unit } from 'mol-model/structure'
import { ValueCell } from 'mol-util/value-cell' import { ValueCell } from 'mol-util/value-cell'
import { Vec2 } from 'mol-math/linear-algebra' import { Vec2 } from 'mol-math/linear-algebra'
import { TextureImage, createTextureImage } from 'mol-gl/renderable/util'; import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
...@@ -72,10 +71,7 @@ export function applyFlagAction(array: Uint8Array, start: number, end: number, a ...@@ -72,10 +71,7 @@ export function applyFlagAction(array: Uint8Array, start: number, end: number, a
return changed return changed
} }
export function createFlags(group: Unit.SymmetryGroup, flagData?: FlagData): FlagData { export function createFlags(count: number, flagData?: FlagData): FlagData {
const instanceCount = group.units.length
const elementCount = group.elements.length
const count = instanceCount * elementCount
const flags = flagData && flagData.tFlag.ref.value.array.length >= count const flags = flagData && flagData.tFlag.ref.value.array.length >= count
? flagData.tFlag.ref.value ? flagData.tFlag.ref.value
: createTextureImage(count, 1) : createTextureImage(count, 1)
......
...@@ -86,7 +86,7 @@ namespace IntGraph { ...@@ -86,7 +86,7 @@ namespace IntGraph {
* builder.addNextEdge(); * builder.addNextEdge();
* builder.assignProperty(property, srcProp[i]); * builder.assignProperty(property, srcProp[i]);
* } * }
* return builder.createGraph({ property }); * return builder.createGraph({ property });
*/ */
addNextEdge() { addNextEdge() {
const a = this.xs[this.current], b = this.ys[this.current]; const a = this.xs[this.current], b = this.ys[this.current];
......
...@@ -23,7 +23,11 @@ namespace Element { ...@@ -23,7 +23,11 @@ namespace Element {
export function createEmptyArray(n: number): Element[] { return new Float64Array(n) as any; } export function createEmptyArray(n: number): Element[] { return new Float64Array(n) as any; }
/** All the information required to access element properties */ /** 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 function Location(unit?: Unit, element?: number): Location { return { unit: unit as any, element: element || 0 }; }
export interface Property<T> { (location: Location): T } export interface Property<T> { (location: Location): T }
export interface Predicate extends Property<boolean> { } export interface Predicate extends Property<boolean> { }
......
...@@ -4,23 +4,27 @@ ...@@ -4,23 +4,27 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { Element } from '../../structure' import { Unit } from '../../structure'
export * from './bonds/intra-data' export * from './bonds/intra-data'
export * from './bonds/intra-compute' export * from './bonds/intra-compute'
interface Bond {
readonly a: Readonly<Element.Location>,
readonly b: Readonly<Element.Location>
}
namespace Bond { 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 { export interface Loci {
readonly kind: 'bond-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'] }; return { kind: 'bond-loci', bonds: bonds as Loci['bonds'] };
} }
......
...@@ -9,6 +9,14 @@ import { Unit, Element, Queries } from 'mol-model/structure'; ...@@ -9,6 +9,14 @@ import { Unit, Element, Queries } from 'mol-model/structure';
import { Bond } from 'mol-model/structure/structure/unit/bonds'; import { Bond } from 'mol-model/structure/structure/unit/bonds';
import { Loci } from 'mol-model/loci'; 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) { export function labelFirst(loci: Loci) {
if(Element.isLoci(loci)) { if(Element.isLoci(loci)) {
const e = loci.elements[0] const e = loci.elements[0]
...@@ -18,7 +26,9 @@ export function labelFirst(loci: Loci) { ...@@ -18,7 +26,9 @@ export function labelFirst(loci: Loci) {
} else if (Bond.isLoci(loci)) { } else if (Bond.isLoci(loci)) {
const bond = loci.bonds[0] const bond = loci.bonds[0]
if (bond) { 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 '' return ''
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
import Viewer from 'mol-view/viewer' import Viewer from 'mol-view/viewer'
import { StateContext } from './state/context'; import { StateContext } from './state/context';
import { Progress } from 'mol-task'; import { Progress } from 'mol-task';
import { MmcifUrlToSpacefill } from './state/transform'; import { MmcifUrlToModel, ModelToStructure, StructureToSpacefill, StructureToBond } from './state/transform';
import { UrlEntity } from './state/entity'; import { UrlEntity } from './state/entity';
import { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; import { SpacefillProps } from 'mol-geo/representation/structure/spacefill';
...@@ -42,9 +42,12 @@ export class Stage { ...@@ -42,9 +42,12 @@ export class Stage {
this.loadMmcifUrl(`../../examples/1cbs_full.bcif`) this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
} }
loadMmcifUrl (url: string) { async loadMmcifUrl (url: string) {
const urlEntity = UrlEntity.ofUrl(this.ctx, url) 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) { loadPdbid (pdbid: string) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment