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

Merge branch 'master' of https://github.com/molstar/molstar into anim-rock

parents 796a034f d61e18e6
No related branches found
No related tags found
No related merge requests found
...@@ -9,6 +9,10 @@ Note that since we don't clearly distinguish between a public and private interf ...@@ -9,6 +9,10 @@ Note that since we don't clearly distinguish between a public and private interf
- Add PDBj as a ``pdb-provider`` option - Add PDBj as a ``pdb-provider`` option
- Move Viewer APP to a separate file to allow use without importing light theme & index.html - Move Viewer APP to a separate file to allow use without importing light theme & index.html
- Add symmetry support for mol2 files (only spacegroup setting 1) - Add symmetry support for mol2 files (only spacegroup setting 1)
- Fix mol2 files element symbol assignment
- Improve bond assignment from ``IndexPairBonds``
- Add ``key`` field for mapping to source data
- Fix assignment of bonds with unphysical length
- Fix label/stats of single atom selection in multi-chain units - Fix label/stats of single atom selection in multi-chain units
- [Breaking] Add rock animation to trackball controls - [Breaking] Add rock animation to trackball controls
- Add ``animate`` to ``TrackballControlsParams``, remove ``spin`` and ``spinSpeed`` - Add ``animate`` to ``TrackballControlsParams``, remove ``spin`` and ``spinSpeed``
......
...@@ -31,8 +31,17 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext) { ...@@ -31,8 +31,17 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext) {
const A = Column.ofConst('A', atoms.count, Column.Schema.str); const A = Column.ofConst('A', atoms.count, Column.Schema.str);
const type_symbol = new Array<string>(atoms.count); const type_symbol = new Array<string>(atoms.count);
let hasAtomType = false;
for (let i = 0; i < atoms.count; ++i) { for (let i = 0; i < atoms.count; ++i) {
type_symbol[i] = guessElementSymbolString(atoms.atom_name.value(i)); if (atoms.atom_type.value(i).includes('.')) {
hasAtomType = true;
break;
}
}
for (let i = 0; i < atoms.count; ++i) {
type_symbol[i] = hasAtomType
? atoms.atom_type.value(i).split('.')[0].toUpperCase()
: guessElementSymbolString(atoms.atom_name.value(i));
} }
const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, { const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
...@@ -77,6 +86,7 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext) { ...@@ -77,6 +86,7 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext) {
if (_models.frameCount > 0) { if (_models.frameCount > 0) {
const indexA = Column.ofIntArray(Column.mapToArray(bonds.origin_atom_id, x => x - 1, Int32Array)); const indexA = Column.ofIntArray(Column.mapToArray(bonds.origin_atom_id, x => x - 1, Int32Array));
const indexB = Column.ofIntArray(Column.mapToArray(bonds.target_atom_id, x => x - 1, Int32Array)); const indexB = Column.ofIntArray(Column.mapToArray(bonds.target_atom_id, x => x - 1, Int32Array));
const key = bonds.bond_id;
const order = Column.ofIntArray(Column.mapToArray(bonds.bond_type, x => { const order = Column.ofIntArray(Column.mapToArray(bonds.bond_type, x => {
switch (x) { switch (x) {
case 'ar': // aromatic case 'ar': // aromatic
...@@ -103,7 +113,7 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext) { ...@@ -103,7 +113,7 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext) {
return BondType.Flag.Covalent; return BondType.Flag.Covalent;
} }
}, Int8Array)); }, Int8Array));
const pairBonds = IndexPairBonds.fromData({ pairs: { indexA, indexB, order, flag }, count: atoms.count }); const pairBonds = IndexPairBonds.fromData({ pairs: { key, indexA, indexB, order, flag }, count: atoms.count });
const first = _models.representative; const first = _models.representative;
IndexPairBonds.Provider.set(first, pairBonds); IndexPairBonds.Provider.set(first, pairBonds);
......
/** /**
* Copyright (c) 2019-2021 Mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2019-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
...@@ -10,9 +10,9 @@ import { Column } from '../../../../mol-data/db'; ...@@ -10,9 +10,9 @@ import { Column } from '../../../../mol-data/db';
import { FormatPropertyProvider } from '../../common/property'; import { FormatPropertyProvider } from '../../common/property';
import { BondType } from '../../../../mol-model/structure/model/types'; import { BondType } from '../../../../mol-model/structure/model/types';
import { ElementIndex } from '../../../../mol-model/structure'; import { ElementIndex } from '../../../../mol-model/structure';
import { DefaultBondMaxRadius } from '../../../../mol-model/structure/structure/unit/bonds/common';
export type IndexPairsProps = { export type IndexPairsProps = {
readonly key: ArrayLike<number>
readonly order: ArrayLike<number> readonly order: ArrayLike<number>
readonly distance: ArrayLike<number> readonly distance: ArrayLike<number>
readonly flag: ArrayLike<BondType.Flag> readonly flag: ArrayLike<BondType.Flag>
...@@ -22,17 +22,19 @@ export type IndexPairBonds = { bonds: IndexPairs, maxDistance: number } ...@@ -22,17 +22,19 @@ export type IndexPairBonds = { bonds: IndexPairs, maxDistance: number }
function getGraph(indexA: ArrayLike<ElementIndex>, indexB: ArrayLike<ElementIndex>, props: Partial<IndexPairsProps>, count: number): IndexPairs { function getGraph(indexA: ArrayLike<ElementIndex>, indexB: ArrayLike<ElementIndex>, props: Partial<IndexPairsProps>, count: number): IndexPairs {
const builder = new IntAdjacencyGraph.EdgeBuilder(count, indexA, indexB); const builder = new IntAdjacencyGraph.EdgeBuilder(count, indexA, indexB);
const key = new Int32Array(builder.slotCount);
const order = new Int8Array(builder.slotCount); const order = new Int8Array(builder.slotCount);
const distance = new Array(builder.slotCount); const distance = new Array(builder.slotCount);
const flag = new Array(builder.slotCount); const flag = new Array(builder.slotCount);
for (let i = 0, _i = builder.edgeCount; i < _i; i++) { for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
builder.addNextEdge(); builder.addNextEdge();
builder.assignProperty(key, props.key ? props.key[i] : -1);
builder.assignProperty(order, props.order ? props.order[i] : 1); builder.assignProperty(order, props.order ? props.order[i] : 1);
builder.assignProperty(distance, props.distance ? props.distance[i] : -1); builder.assignProperty(distance, props.distance ? props.distance[i] : -1);
builder.assignProperty(flag, props.flag ? props.flag[i] : BondType.Flag.Covalent); builder.assignProperty(flag, props.flag ? props.flag[i] : BondType.Flag.Covalent);
} }
return builder.createGraph({ order, distance, flag }); return builder.createGraph({ key, order, distance, flag });
} }
export namespace IndexPairBonds { export namespace IndexPairBonds {
...@@ -45,15 +47,33 @@ export namespace IndexPairBonds { ...@@ -45,15 +47,33 @@ export namespace IndexPairBonds {
export type Data = { export type Data = {
pairs: { pairs: {
indexA: Column<number>, indexA: Column<number>,
indexB: Column<number> indexB: Column<number>,
key?: Column<number>,
order?: Column<number>, order?: Column<number>,
/**
* Useful for bonds in periodic cells. That is, only bonds within the given
* distance are added. This allows for bond between periodic image but
* avoids unwanted bonds with wrong distances. If negative, test using the
* `maxDistance` option from `Props`.
*/
distance?: Column<number>, distance?: Column<number>,
flag?: Column<BondType.Flag>, flag?: Column<BondType.Flag>,
}, },
count: number count: number
} }
export const DefaultProps = { maxDistance: DefaultBondMaxRadius }; export const DefaultProps = {
/**
* If negative, test using element-based threshold, otherwise distance in Angstrom.
*
* This option exists to handle bonds in periodic cells. For systems that are
* made from beads (as opposed to atomic elements), set to a specific distance.
*
* Note that `Data` has a `distance` field which allows specifying a distance
* for each bond individually which takes precedence over this option.
*/
maxDistance: -1
};
export type Props = typeof DefaultProps export type Props = typeof DefaultProps
export function fromData(data: Data, props: Partial<Props> = {}): IndexPairBonds { export function fromData(data: Data, props: Partial<Props> = {}): IndexPairBonds {
...@@ -61,11 +81,12 @@ export namespace IndexPairBonds { ...@@ -61,11 +81,12 @@ export namespace IndexPairBonds {
const { pairs, count } = data; const { pairs, count } = data;
const indexA = pairs.indexA.toArray() as ArrayLike<ElementIndex>; const indexA = pairs.indexA.toArray() as ArrayLike<ElementIndex>;
const indexB = pairs.indexB.toArray() as ArrayLike<ElementIndex>; const indexB = pairs.indexB.toArray() as ArrayLike<ElementIndex>;
const key = pairs.key && pairs.key.toArray();
const order = pairs.order && pairs.order.toArray(); const order = pairs.order && pairs.order.toArray();
const distance = pairs.distance && pairs.distance.toArray(); const distance = pairs.distance && pairs.distance.toArray();
const flag = pairs.flag && pairs.flag.toArray(); const flag = pairs.flag && pairs.flag.toArray();
return { return {
bonds: getGraph(indexA, indexB, { order, distance, flag }, count), bonds: getGraph(indexA, indexB, { key, order, distance, flag }, count),
maxDistance: p.maxDistance maxDistance: p.maxDistance
}; };
} }
......
/** /**
* Copyright (c) 2017-2021 Mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2017-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
...@@ -110,6 +110,15 @@ export function getElementThreshold(i: number) { ...@@ -110,6 +110,15 @@ export function getElementThreshold(i: number) {
return r; return r;
} }
export function getPairingThreshold(elementIndexA: number, elementIndexB: number, thresholdA: number, thresholdB: number) {
const thresholdAB = getElementPairThreshold(elementIndexA, elementIndexB);
return thresholdAB > 0
? thresholdAB
: elementIndexB < 0
? thresholdA
: (thresholdA + thresholdB) / 1.95; // not sure if avg or min but max is too big
}
const H_ID = __ElementIndex['H']!; const H_ID = __ElementIndex['H']!;
export function isHydrogen(i: number) { export function isHydrogen(i: number) {
return i === H_ID; return i === H_ID;
......
/** /**
* Copyright (c) 2017-2021 Mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2017-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
import { BondType, MoleculeType } from '../../../model/types'; import { BondType, MoleculeType } from '../../../model/types';
import { Structure } from '../../structure'; import { Structure } from '../../structure';
import { Unit } from '../../unit'; import { Unit } from '../../unit';
import { getElementIdx, getElementPairThreshold, getElementThreshold, isHydrogen, BondComputationProps, MetalsSet, DefaultBondComputationProps } from './common'; import { getElementIdx, getElementThreshold, isHydrogen, BondComputationProps, MetalsSet, DefaultBondComputationProps, getPairingThreshold } from './common';
import { InterUnitBonds, InterUnitEdgeProps } from './data'; import { InterUnitBonds, InterUnitEdgeProps } from './data';
import { SortedArray } from '../../../../../mol-data/int'; import { SortedArray } from '../../../../../mol-data/int';
import { Vec3, Mat4 } from '../../../../../mol-math/linear-algebra'; import { Vec3, Mat4 } from '../../../../../mol-math/linear-algebra';
...@@ -82,11 +82,31 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput ...@@ -82,11 +82,31 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
const _bI = SortedArray.indexOf(unitB.elements, bI) as StructureElement.UnitIndex; const _bI = SortedArray.indexOf(unitB.elements, bI) as StructureElement.UnitIndex;
if (_bI < 0) continue; if (_bI < 0) continue;
if (type_symbolA.value(aI) === 'H' && type_symbolB.value(bI) === 'H') continue;
const aeI = getElementIdx(type_symbolA.value(aI));
const beI = getElementIdx(type_symbolA.value(bI));
const d = distance[i]; const d = distance[i];
const dist = getDistance(unitA, aI, unitB, bI); const dist = getDistance(unitA, aI, unitB, bI);
if ((d !== -1 && equalEps(dist, d, 0.5)) || dist < maxDistance) {
let add = false;
if (d >= 0) {
add = equalEps(dist, d, 0.3);
} else if (maxDistance >= 0) {
add = dist < maxDistance;
} else {
const pairingThreshold = getPairingThreshold(
aeI, beI, getElementThreshold(aeI), getElementThreshold(beI)
);
add = dist < pairingThreshold;
if (isHydrogen(aeI) && isHydrogen(beI)) {
// TODO handle molecular hydrogen
add = false;
}
}
if (add) {
builder.add(_aI, _bI, { order: order[i], flag: flag[i] }); builder.add(_aI, _bI, { order: order[i], flag: flag[i] });
} }
} }
...@@ -155,13 +175,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput ...@@ -155,13 +175,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
const dist = Math.sqrt(squaredDistances[ni]); const dist = Math.sqrt(squaredDistances[ni]);
if (dist === 0) continue; if (dist === 0) continue;
const thresholdAB = getElementPairThreshold(aeI, beI); const pairingThreshold = getPairingThreshold(aeI, beI, thresholdA, getElementThreshold(beI));
const pairingThreshold = thresholdAB > 0
? thresholdAB
: beI < 0
? thresholdA
: (thresholdA + getElementThreshold(beI)) / 1.95; // not sure if avg or min but max is too big
if (dist <= pairingThreshold) { if (dist <= pairingThreshold) {
const atomIdB = label_atom_idB.value(bI); const atomIdB = label_atom_idB.value(bI);
const compIdB = label_comp_idB.value(residueIndexB[bI]); const compIdB = label_comp_idB.value(residueIndexB[bI]);
......
/** /**
* Copyright (c) 2017-2021 Mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2017-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
...@@ -9,7 +9,7 @@ import { BondType } from '../../../model/types'; ...@@ -9,7 +9,7 @@ import { BondType } from '../../../model/types';
import { IntraUnitBonds } from './data'; import { IntraUnitBonds } from './data';
import { Unit } from '../../unit'; import { Unit } from '../../unit';
import { IntAdjacencyGraph } from '../../../../../mol-math/graph'; import { IntAdjacencyGraph } from '../../../../../mol-math/graph';
import { BondComputationProps, getElementIdx, MetalsSet, getElementThreshold, isHydrogen, getElementPairThreshold, DefaultBondComputationProps } from './common'; import { BondComputationProps, getElementIdx, MetalsSet, getElementThreshold, isHydrogen, DefaultBondComputationProps, getPairingThreshold } from './common';
import { SortedArray } from '../../../../../mol-data/int'; import { SortedArray } from '../../../../../mol-data/int';
import { getIntraBondOrderFromTable } from '../../../model/properties/atomic/bonds'; import { getIntraBondOrderFromTable } from '../../../model/properties/atomic/bonds';
import { StructureElement } from '../../element'; import { StructureElement } from '../../element';
...@@ -62,7 +62,8 @@ function findIndexPairBonds(unit: Unit.Atomic) { ...@@ -62,7 +62,8 @@ function findIndexPairBonds(unit: Unit.Atomic) {
for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) { for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
const aI = atoms[_aI]; const aI = atoms[_aI];
const isHa = type_symbol.value(aI) === 'H'; const aeI = getElementIdx(type_symbol.value(aI));
const isHa = isHydrogen(aeI);
const srcA = sourceIndex.value(aI); const srcA = sourceIndex.value(aI);
...@@ -72,11 +73,30 @@ function findIndexPairBonds(unit: Unit.Atomic) { ...@@ -72,11 +73,30 @@ function findIndexPairBonds(unit: Unit.Atomic) {
const _bI = SortedArray.indexOf(unit.elements, bI) as StructureElement.UnitIndex; const _bI = SortedArray.indexOf(unit.elements, bI) as StructureElement.UnitIndex;
if (_bI < 0) continue; if (_bI < 0) continue;
if (isHa && type_symbol.value(bI) === 'H') continue;
const beI = getElementIdx(type_symbol.value(bI));
const d = distance[i]; const d = distance[i];
const dist = getDistance(unit, aI, bI); const dist = getDistance(unit, aI, bI);
if ((d !== -1 && equalEps(dist, d, 0.5)) || dist < maxDistance) {
let add = false;
if (d >= 0) {
add = equalEps(dist, d, 0.3);
} else if (maxDistance >= 0) {
add = dist < maxDistance;
} else {
const pairingThreshold = getPairingThreshold(
aeI, beI, getElementThreshold(aeI), getElementThreshold(beI)
);
add = dist < pairingThreshold;
if (isHa && isHydrogen(beI)) {
// TODO handle molecular hydrogen
add = false;
}
}
if (add) {
atomA[atomA.length] = _aI; atomA[atomA.length] = _aI;
atomB[atomB.length] = _bI; atomB[atomB.length] = _bI;
orders[orders.length] = order[i]; orders[orders.length] = order[i];
...@@ -214,13 +234,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon ...@@ -214,13 +234,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
const dist = Math.sqrt(squaredDistances[ni]); const dist = Math.sqrt(squaredDistances[ni]);
if (dist === 0) continue; if (dist === 0) continue;
const thresholdAB = getElementPairThreshold(aeI, beI); const pairingThreshold = getPairingThreshold(aeI, beI, thresholdA, getElementThreshold(beI));
const pairingThreshold = thresholdAB > 0
? thresholdAB
: beI < 0
? thresholdA
: (thresholdA + getElementThreshold(beI)) / 1.95; // not sure if avg or min but max is too big
if (dist <= pairingThreshold) { if (dist <= pairingThreshold) {
atomA[atomA.length] = _aI; atomA[atomA.length] = _aI;
atomB[atomB.length] = _bI; atomB[atomB.length] = _bI;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment