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

add ellipsoid repr showing mmcif thermal displacement

parent 9113d6d1
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
import { MeshBuilder } from '../mesh-builder';
import { getSphere } from './sphere';
const tmpEllipsoidMat = Mat4.identity()
const tmpVec = Vec3()
function setEllipsoidMat(m: Mat4, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3) {
Vec3.add(tmpVec, center, dirMajor)
Mat4.targetTo(m, center, tmpVec, dirMinor)
Mat4.setTranslation(m, center)
return Mat4.scale(m, m, radiusScale)
}
export function addEllipsoid(state: MeshBuilder.State, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3, detail: number) {
MeshBuilder.addPrimitive(state, setEllipsoidMat(tmpEllipsoidMat, center, dirMajor, dirMinor, radiusScale), getSphere(detail))
}
\ No newline at end of file
...@@ -16,7 +16,7 @@ function setSphereMat(m: Mat4, center: Vec3, radius: number) { ...@@ -16,7 +16,7 @@ function setSphereMat(m: Mat4, center: Vec3, radius: number) {
return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius) return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius)
} }
function getSphere(detail: number) { export function getSphere(detail: number) {
let sphere = sphereMap.get(detail) let sphere = sphereMap.get(detail)
if (sphere === undefined) { if (sphere === undefined) {
sphere = Sphere(detail) sphere = Sphere(detail)
......
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Table } from '../../../mol-data/db';
import { Model, CustomPropertyDescriptor } from '../../../mol-model/structure';
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
import { CifWriter } from '../../../mol-io/writer/cif';
export interface AtomSiteAnisotrop {
data: Table<AtomSiteAnisotrop.Schema['atom_site_anisotrop']>
/** maps atom_site-index to atom_site_anisotrop-index */
elementToAnsiotrop: Int32Array
}
export namespace AtomSiteAnisotrop {
export function getAtomSiteAnisotrop(model: Model) {
if (model.sourceData.kind !== 'mmCIF') return void 0;
const { atom_site_anisotrop } = model.sourceData.data
return Table.ofColumns(Schema.atom_site_anisotrop, atom_site_anisotrop);
}
export const PropName = '__AtomSiteAnisotrop__';
export function get(model: Model): AtomSiteAnisotrop | undefined {
if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]
if (!model.customProperties.has(Descriptor)) return void 0;
const data = getAtomSiteAnisotrop(model);
if (!data) return void 0;
const prop = { data, elementToAnsiotrop: getElementToAnsiotrop(model, data) }
set(model, prop)
return prop;
}
function set(model: Model, prop: AtomSiteAnisotrop) {
(model._staticPropertyData[PropName] as AtomSiteAnisotrop) = prop;
}
export const Schema = { atom_site_anisotrop: mmCIF_Schema['atom_site_anisotrop'] };
export type Schema = typeof Schema
export const Descriptor: CustomPropertyDescriptor = {
isStatic: true,
name: 'atom_site_anisotrop',
cifExport: {
prefix: '',
categories: [{
name: 'atom_site_anisotrop',
instance(ctx) {
const atom_site_anisotrop = getAtomSiteAnisotrop(ctx.firstModel);
if (!atom_site_anisotrop) return CifWriter.Category.Empty;
return CifWriter.Category.ofTable(atom_site_anisotrop);
}
}]
}
};
function getElementToAnsiotrop(model: Model, data: Table<Schema['atom_site_anisotrop']>) {
const { atomId } = model.atomicConformation
const atomIdToElement = new Int32Array(atomId.rowCount)
atomIdToElement.fill(-1)
for (let i = 0, il = atomId.rowCount; i < il; i++) {
atomIdToElement[atomId.value(i)] = i
}
const { id } = data
const elementToAnsiotrop = new Int32Array(atomId.rowCount)
elementToAnsiotrop.fill(-1)
for (let i = 0, il = id.rowCount; i < il; ++i) {
const ei = atomIdToElement[id.value(i)]
if (ei !== -1) elementToAnsiotrop[ei] = i
}
return elementToAnsiotrop
}
export async function attachFromMmCif(model: Model) {
if (model.customProperties.has(Descriptor)) return true;
if (model.sourceData.kind !== 'mmCIF') return false;
const atomSiteAnisotrop = getAtomSiteAnisotrop(model);
if (!atomSiteAnisotrop || atomSiteAnisotrop._rowCount === 0) return false;
model.customProperties.add(Descriptor);
return true;
}
}
\ No newline at end of file
...@@ -29,6 +29,7 @@ import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, Saccha ...@@ -29,6 +29,7 @@ import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, Saccha
import mmCIF_Format = ModelFormat.mmCIF import mmCIF_Format = ModelFormat.mmCIF
import { memoize1 } from '../../../mol-util/memoize'; import { memoize1 } from '../../../mol-util/memoize';
import { ElementIndex, EntityIndex } from '../../../mol-model/structure/model'; import { ElementIndex, EntityIndex } from '../../../mol-model/structure/model';
import { AtomSiteAnisotrop } from './anisotropic';
export async function _parse_mmCif(format: mmCIF_Format, ctx: RuntimeContext) { export async function _parse_mmCif(format: mmCIF_Format, ctx: RuntimeContext) {
const formatData = getFormatData(format) const formatData = getFormatData(format)
...@@ -279,6 +280,7 @@ function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatD ...@@ -279,6 +280,7 @@ function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatD
function attachProps(model: Model) { function attachProps(model: Model) {
ComponentBond.attachFromMmCif(model); ComponentBond.attachFromMmCif(model);
StructConn.attachFromMmCif(model); StructConn.attachFromMmCif(model);
AtomSiteAnisotrop.attachFromMmCif(model);
} }
function findModelEnd(num: Column<number>, startIndex: number) { function findModelEnd(num: Column<number>, startIndex: number) {
......
...@@ -16,6 +16,7 @@ import { PointRepresentationProvider } from './representation/point'; ...@@ -16,6 +16,7 @@ import { PointRepresentationProvider } from './representation/point';
import { StructureRepresentationState } from './representation'; import { StructureRepresentationState } from './representation';
import { PuttyRepresentationProvider } from './representation/putty'; import { PuttyRepresentationProvider } from './representation/putty';
import { MolecularSurfaceRepresentationProvider } from './representation/molecular-surface'; import { MolecularSurfaceRepresentationProvider } from './representation/molecular-surface';
import { EllipsoidRepresentationProvider } from './representation/ellipsoid';
export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> { export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> {
constructor() { constructor() {
...@@ -32,6 +33,7 @@ export const BuiltInStructureRepresentations = { ...@@ -32,6 +33,7 @@ export const BuiltInStructureRepresentations = {
'ball-and-stick': BallAndStickRepresentationProvider, 'ball-and-stick': BallAndStickRepresentationProvider,
'carbohydrate': CarbohydrateRepresentationProvider, 'carbohydrate': CarbohydrateRepresentationProvider,
'distance-restraint': DistanceRestraintRepresentationProvider, 'distance-restraint': DistanceRestraintRepresentationProvider,
'ellipsoid': EllipsoidRepresentationProvider,
'gaussian-surface': GaussianSurfaceRepresentationProvider, 'gaussian-surface': GaussianSurfaceRepresentationProvider,
// 'gaussian-volume': GaussianVolumeRepresentationProvider, // TODO disabled for now, needs more work // 'gaussian-volume': GaussianVolumeRepresentationProvider, // TODO disabled for now, needs more work
'molecular-surface': MolecularSurfaceRepresentationProvider, 'molecular-surface': MolecularSurfaceRepresentationProvider,
......
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { RepresentationParamsGetter, RepresentationContext, Representation } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider } from '../../../mol-repr/structure/representation';
import { EllipsoidMeshParams, EllipsoidMeshVisual } from '../visual/ellipsoid-mesh';
import { UnitKind, UnitKindOptions } from '../../../mol-repr/structure/visual/util/common';
import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/mmcif/anisotropic';
const EllipsoidVisuals = {
'ellipsoid-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, EllipsoidParams>) => UnitsRepresentation('Ellipsoid Mesh', ctx, getParams, EllipsoidMeshVisual),
}
export const EllipsoidParams = {
...EllipsoidMeshParams,
unitKinds: PD.MultiSelect<UnitKind>(['atomic'], UnitKindOptions),
}
export type EllipsoidParams = typeof EllipsoidMeshParams
export function getEllipsoidParams(ctx: ThemeRegistryContext, structure: Structure) {
return PD.clone(EllipsoidMeshParams)
}
export type EllipsoidRepresentation = StructureRepresentation<EllipsoidParams>
export function EllipsoidRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, EllipsoidParams>): EllipsoidRepresentation {
return Representation.createMulti('Ellipsoid', ctx, getParams, StructureRepresentationStateBuilder, EllipsoidVisuals as unknown as Representation.Def<Structure, EllipsoidParams>)
}
export const EllipsoidRepresentationProvider: StructureRepresentationProvider<EllipsoidParams> = {
label: 'Ellipsoid',
description: 'Displays anisotropic displacement ellipsoids of atomic elements.',
factory: EllipsoidRepresentation,
getParams: getEllipsoidParams,
defaultValues: PD.getDefaultValues(EllipsoidMeshParams),
defaultColorTheme: 'element-symbol',
defaultSizeTheme: 'uniform',
isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => m.customProperties.has(AtomSiteAnisotrop.Descriptor))
}
\ No newline at end of file
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../../../mol-repr/structure/units-visual';
import { ElementIterator, getElementLoci, eachElement } from '../../../mol-repr/structure/visual/util/element';
import { VisualUpdateState } from '../../../mol-repr/util';
import { VisualContext } from '../../../mol-repr/visual';
import { Unit, Structure, StructureElement } from '../../../mol-model/structure';
import { Theme } from '../../../mol-theme/theme';
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
import { sphereVertexCount } from '../../../mol-geo/primitive/sphere';
import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
import { Vec3, Mat3, Tensor } from '../../../mol-math/linear-algebra';
import { isHydrogen } from '../../../mol-repr/structure/visual/util/common';
import { addEllipsoid } from '../../../mol-geo/geometry/mesh/builder/ellipsoid';
import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/mmcif/anisotropic'
export const EllipsoidMeshParams = {
...UnitsMeshParams,
sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
ignoreHydrogens: PD.Boolean(false),
}
export type EllipsoidMeshParams = typeof EllipsoidMeshParams
export function EllipsoidMeshVisual(materialId: number): UnitsVisual<EllipsoidMeshParams> {
return UnitsMeshVisual<EllipsoidMeshParams>({
defaultProps: PD.getDefaultValues(EllipsoidMeshParams),
createGeometry: createEllipsoidMesh,
createLocationIterator: ElementIterator.fromGroup,
getLoci: getElementLoci,
eachLocation: eachElement,
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<EllipsoidMeshParams>, currentProps: PD.Values<EllipsoidMeshParams>) => {
state.createGeometry = (
newProps.sizeFactor !== currentProps.sizeFactor ||
newProps.detail !== currentProps.detail ||
newProps.ignoreHydrogens !== currentProps.ignoreHydrogens
)
}
}, materialId)
}
//
export interface EllipsoidMeshProps {
detail: number,
sizeFactor: number,
ignoreHydrogens: boolean,
}
export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: EllipsoidMeshProps, mesh?: Mesh): Mesh {
const { detail, sizeFactor } = props
const { elements, model } = unit;
const elementCount = elements.length;
const vertexCount = elementCount * sphereVertexCount(detail)
const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh)
const atomSiteAnisotrop = AtomSiteAnisotrop.get(model)
if (!atomSiteAnisotrop) return Mesh.createEmpty(mesh)
const v = Vec3()
const m = Mat3()
const eigenvalues = Vec3()
const eigenvector1 = Vec3()
const eigenvector2 = Vec3()
const { elementToAnsiotrop, data } = atomSiteAnisotrop
const { U } = data
const space = data._schema.U.space
const pos = unit.conformation.invariantPosition
const l = StructureElement.Location.create()
l.unit = unit
for (let i = 0; i < elementCount; i++) {
const ei = elements[i]
const ai = elementToAnsiotrop[ei]
if (ai === -1) continue
if (props.ignoreHydrogens && isHydrogen(unit, ei)) continue
l.element = ei
pos(ei, v)
builderState.currentGroup = i
Tensor.toMat3(m, space, U.value(ai))
Mat3.symmtricFromLower(m, m)
Mat3.symmetricEigenvalues(eigenvalues, m)
Mat3.eigenvector(eigenvector1, m, eigenvalues[1])
Mat3.eigenvector(eigenvector2, m, eigenvalues[2])
for (let j = 0; j < 3; ++j) {
// show 50% probability surface, needs sqrt as U matrix is in angstrom-squared
// take abs of eigenvalue to avoid reflection
// multiply by given size-factor
eigenvalues[j] = sizeFactor * 1.5958 * Math.sqrt(Math.abs(eigenvalues[j]))
}
addEllipsoid(builderState, v, eigenvector2, eigenvector1, eigenvalues, detail)
}
return MeshBuilder.getMesh(builderState)
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment