diff --git a/.vscode/settings.json b/.vscode/settings.json index 9f20238be3337de52d4b5264853a36f5bdf719b8..1fcd8d6e6dfa22aacc0161cadd9eddcd5c007499 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,7 @@ "*.vert.ts": "glsl", "*.gql.ts": "graphql" }, + "eslint.options": { + "ignorePattern": ["webpack.config.js", "scripts/*"], + } } \ No newline at end of file diff --git a/src/mol-model-props/computed/interactions/charged.ts b/src/mol-model-props/computed/interactions/charged.ts index 75979f75ebf27a6226a6f1511b8d9366a1bc3dbe..7f22026d8e1141fb143b8dfbf0dd592b8063d923 100644 --- a/src/mol-model-props/computed/interactions/charged.ts +++ b/src/mol-model-props/computed/interactions/charged.ts @@ -240,22 +240,18 @@ const tmpVecD = Vec3() function getNormal(out: Vec3, info: Features.Info) { const { unit, feature, offsets, members } = info const { elements } = unit - const { x, y, z } = unit.model.atomicConformation const i = offsets[feature] - const aI = elements[members[i]] - const bI = elements[members[i + 1]] - const cI = elements[members[i + 2]] - Vec3.set(tmpVecA, x[aI], y[aI], z[aI]) - Vec3.set(tmpVecB, x[bI], y[bI], z[bI]) - Vec3.set(tmpVecC, x[cI], y[cI], z[cI]) + info.unit.conformation.position(elements[members[i]], tmpVecA) + info.unit.conformation.position(elements[members[i + 1]], tmpVecB) + info.unit.conformation.position(elements[members[i + 2]], tmpVecC) return Vec3.triangleNormal(out, tmpVecA, tmpVecB, tmpVecC) } const getOffset = function (infoA: Features.Info, infoB: Features.Info, normal: Vec3) { - Vec3.set(tmpVecA, infoA.x[infoA.feature], infoA.y[infoA.feature], infoA.z[infoA.feature]) - Vec3.set(tmpVecB, infoB.x[infoB.feature], infoB.y[infoB.feature], infoB.z[infoB.feature]) + Features.position(tmpVecA, infoA) + Features.position(tmpVecB, infoB) Vec3.sub(tmpVecC, tmpVecA, tmpVecB) diff --git a/src/mol-model-props/computed/interactions/contacts.ts b/src/mol-model-props/computed/interactions/contacts.ts index 9c85398e1d6058d1d80a4361454d465462c5a1bf..57967b0c19e8210b4ed905fa52162ad76a6d7287 100644 --- a/src/mol-model-props/computed/interactions/contacts.ts +++ b/src/mol-model-props/computed/interactions/contacts.ts @@ -65,12 +65,6 @@ function isMember(element: StructureElement.UnitIndex, info: Features.Info) { return false } -function setPosition(out: Vec3, info: Features.Info) { - Vec3.set(out, info.x[info.feature], info.y[info.feature], info.z[info.feature]) - Vec3.transformMat4(out, out, info.unit.conformation.operator.matrix) - return out -} - const tmpVec = Vec3() const tmpVecA = Vec3() const tmpVecB = Vec3() @@ -81,8 +75,8 @@ function checkLineOfSight(structure: Structure, infoA: Features.Info, infoB: Fea const indexA = infoA.members[infoA.offsets[featureA]] const indexB = infoB.members[infoB.offsets[featureB]] - setPosition(tmpVecA, infoA) - setPosition(tmpVecB, infoB) + Features.position(tmpVecA, infoA) + Features.position(tmpVecB, infoB) Vec3.scale(tmpVec, Vec3.add(tmpVec, tmpVecA, tmpVecB), 0.5) const distMax = distFactor * MAX_LINE_OF_SIGHT_DISTANCE diff --git a/src/mol-model-props/computed/interactions/features.ts b/src/mol-model-props/computed/interactions/features.ts index 9bf69761a79ab977636238509c77ba0a44e8cfa9..e7763a2e6679fb272309951ac9e8d4fe85d9ad4d 100644 --- a/src/mol-model-props/computed/interactions/features.ts +++ b/src/mol-model-props/computed/interactions/features.ts @@ -161,6 +161,22 @@ namespace Features { } } + export function position(out: Vec3, info: Info) { + Vec3.set(out, info.x[info.feature], info.y[info.feature], info.z[info.feature]) + Vec3.transformMat4(out, out, info.unit.conformation.operator.matrix) + return out + } + + const tmpVecA = Vec3() + const tmpVecB = Vec3() + export function distance(infoA: Info, infoB: Info) { + const elementA = infoA.members[infoA.offsets[infoA.feature]] + const elementB = infoB.members[infoB.offsets[infoB.feature]] + infoA.unit.conformation.position(infoA.unit.elements[elementA], tmpVecA) + infoB.unit.conformation.position(infoB.unit.elements[elementB], tmpVecB) + return Vec3.distance(tmpVecA, tmpVecB) + } + export interface Provider { types: Set<FeatureType> add: (structure: Structure, unit: Unit.Atomic, featuresBuilder: FeaturesBuilder) => void diff --git a/src/mol-model-props/computed/interactions/hydrogen-bonds.ts b/src/mol-model-props/computed/interactions/hydrogen-bonds.ts index f6021978c047171c53ed3df3a15e61c76a25db18..37bc77ffc86f158c637861023661a5a4ba680b32 100644 --- a/src/mol-model-props/computed/interactions/hydrogen-bonds.ts +++ b/src/mol-model-props/computed/interactions/hydrogen-bonds.ts @@ -68,8 +68,11 @@ function addUnitHydrogenDonors(structure: Structure, unit: Unit.Atomic, builder: const { totalH } = getUnitValenceModel(structure, unit) const { elements } = unit const { x, y, z } = unit.model.atomicConformation + const { elementAromaticRingIndices } = unit.rings for (let i = 0 as StructureElement.UnitIndex, il = elements.length; i < il; ++i) { + if (elementAromaticRingIndices.has(i)) continue; + const element = typeSymbol(unit, i) if (( // include both nitrogen atoms in histidine due to @@ -99,7 +102,7 @@ function addUnitWeakHydrogenDonors(structure: Structure, unit: Unit.Atomic, buil ( bondToElementCount(structure, unit, i, Elements.N) > 0 || bondToElementCount(structure, unit, i, Elements.O) > 0 || - inAromaticRingWithElectronNegativeElement(structure, unit, i) + inAromaticRingWithElectronNegativeElement(unit, i) ) ) { builder.add(FeatureType.WeakHydrogenDonor, FeatureGroup.None, x[elements[i]], y[elements[i]], z[elements[i]], i) @@ -107,27 +110,21 @@ function addUnitWeakHydrogenDonors(structure: Structure, unit: Unit.Atomic, buil } } -function inAromaticRingWithElectronNegativeElement(structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex) { - return false // TODO - // if (!a.isAromatic()) return false - - // const ringData = a.residueType.getRings() - // if (!ringData) return false - - // let hasElement = false - // const rings = ringData.rings - // rings.forEach(ring => { - // if (hasElement) return // already found one - // if (ring.some(idx => (a.index - a.residueAtomOffset) === idx)) { // in ring - // hasElement = ring.some(idx => { - // const atomTypeId = a.residueType.atomTypeIdList[ idx ] - // const number = a.atomMap.get(atomTypeId).number - // return number === Elements.N || number === Elements.O - // }) - // } - // }) - - // return hasElement +function inAromaticRingWithElectronNegativeElement(unit: Unit.Atomic, index: StructureElement.UnitIndex) { + const { elementAromaticRingIndices, all } = unit.rings + const ringIndices = elementAromaticRingIndices.get(index) + if (ringIndices === undefined) return false + + for (let i = 0, il = ringIndices.length; i < il; ++i) { + const ring = all[ringIndices[i]] + for (let j = 0, jl = ring.length; j < jl; ++j) { + const element = typeSymbol(unit, ring[j]) + if (element === Elements.N || element === Elements.O) { + return true + } + } + } + return false } /** @@ -137,12 +134,15 @@ function addUnitHydrogenAcceptors(structure: Structure, unit: Unit.Atomic, build const { charge, implicitH, idealGeometry } = getUnitValenceModel(structure, unit) const { elements } = unit const { x, y, z } = unit.model.atomicConformation + const { elementAromaticRingIndices } = unit.rings const add = (i: StructureElement.UnitIndex) => { builder.add(FeatureType.HydrogenAcceptor, FeatureGroup.None, x[elements[i]], y[elements[i]], z[elements[i]], i) } for (let i = 0 as StructureElement.UnitIndex, il = elements.length; i < il; ++i) { + if (elementAromaticRingIndices.has(i)) continue; + const element = typeSymbol(unit, i) if (element === Elements.O) { // Basically assume all oxygen atoms are acceptors! diff --git a/src/mol-model-props/computed/interactions/refine.ts b/src/mol-model-props/computed/interactions/refine.ts index 4e1dacbf8ef88a2660195421abd141c6dd2ff71b..2bda57a2279625f5a044a14b0c9b103b90f15378 100644 --- a/src/mol-model-props/computed/interactions/refine.ts +++ b/src/mol-model-props/computed/interactions/refine.ts @@ -8,7 +8,6 @@ import { Interactions } from './interactions'; import { InteractionType, InteractionFlag, InteractionsIntraContacts, FeatureType, InteractionsInterContacts } from './common'; -import { Vec3 } from '../../../mol-math/linear-algebra'; import { Unit, Structure } from '../../../mol-model/structure'; import { Features } from './features'; @@ -91,22 +90,17 @@ function hydrophobicRefiner(structure: Structure, interactions: Interactions): C } } - const pA = Vec3() - const pB = Vec3() - function handleEdge(edge: number, infoA: Features.Info, infoB: Features.Info, map: Map<string, [number, number]>, set: (i: number) => void) { const elementA = infoA.members[infoA.offsets[infoA.feature]] const elementB = infoB.members[infoB.offsets[infoB.feature]] const residueA = infoA.unit.getResidueIndex(elementA) const residueB = infoB.unit.getResidueIndex(elementB) - infoA.unit.conformation.position(infoA.unit.elements[elementA], pA) - infoB.unit.conformation.position(infoB.unit.elements[elementB], pB) - const dist = Vec3.distance(pA, pB) - const keyA = `${elementA}|${infoA.unit.id}|${residueB}|${infoB.unit.id}|A` const keyB = `${elementB}|${infoB.unit.id}|${residueA}|${infoA.unit.id}|B` + const dist = Features.distance(infoA, infoB) + handleResidueContact(dist, edge, keyA, map, set) handleResidueContact(dist, edge, keyB, map, set) } diff --git a/src/mol-model/structure/structure/unit/rings.ts b/src/mol-model/structure/structure/unit/rings.ts index d9f1024ef8a600fa073247726020ada8b9ccff16..40296716a076b33880557c844ed2193dadeb3192 100644 --- a/src/mol-model/structure/structure/unit/rings.ts +++ b/src/mol-model/structure/structure/unit/rings.ts @@ -25,6 +25,7 @@ class UnitRings { private _byFingerprint?: ReadonlyMap<UnitRing.Fingerprint, ReadonlyArray<UnitRings.Index>>; private _index?: { readonly elementRingIndices: ReadonlyMap<StructureElement.UnitIndex, UnitRings.Index[]>, + readonly elementAromaticRingIndices: ReadonlyMap<StructureElement.UnitIndex, UnitRings.Index[]>, readonly ringComponentIndex: ReadonlyArray<UnitRings.ComponentIndex>, readonly ringComponents: ReadonlyArray<ReadonlyArray<UnitRings.Index>> }; @@ -32,7 +33,7 @@ class UnitRings { private get index() { if (this._index) return this._index; - this._index = createIndex(this.all); + this._index = createIndex(this.all, this.aromaticRings); return this._index; } @@ -42,11 +43,15 @@ class UnitRings { return this._byFingerprint; } - /** Maps atom index inside a Unit to the smallest ring index (an atom can be part of more than one ring) */ + /** Maps atom index inside a Unit to ring indices (an atom can be part of more than one ring) */ get elementRingIndices() { return this.index.elementRingIndices; } + get elementAromaticRingIndices() { + return this.index.elementAromaticRingIndices; + } + /** Maps UnitRings.Index to index to ringComponents */ get ringComponentIndex() { return this.index.ringComponentIndex; diff --git a/src/mol-model/structure/structure/unit/rings/compute.ts b/src/mol-model/structure/structure/unit/rings/compute.ts index 177ed65a17098855d5f90be8badd07af9af55a15..db31f0f6900e95aeeb729f0c9e3cafbe9eaaa205 100644 --- a/src/mol-model/structure/structure/unit/rings/compute.ts +++ b/src/mol-model/structure/structure/unit/rings/compute.ts @@ -253,8 +253,9 @@ function buildFinderprint(elements: string[], offset: number) { type RingIndex = import('../rings').UnitRings.Index type RingComponentIndex = import('../rings').UnitRings.ComponentIndex -export function createIndex(rings: ArrayLike<SortedArray<StructureElement.UnitIndex>>) { +export function createIndex(rings: ArrayLike<SortedArray<StructureElement.UnitIndex>>, aromaticRings: ReadonlyArray<RingIndex>) { const elementRingIndices: Map<StructureElement.UnitIndex, RingIndex[]> = new Map(); + const elementAromaticRingIndices: Map<StructureElement.UnitIndex, RingIndex[]> = new Map(); // for each ring atom, assign all rings that it is present in for (let rI = 0 as RingIndex, _rI = rings.length; rI < _rI; rI++) { @@ -266,6 +267,17 @@ export function createIndex(rings: ArrayLike<SortedArray<StructureElement.UnitIn } } + // for each ring atom, assign all aromatic rings that it is present in + for (let aI = 0, _aI = aromaticRings.length; aI < _aI; aI++) { + const rI = aromaticRings[aI] + const r = rings[rI]; + for (let i = 0, _i = r.length; i < _i; i++) { + const e = r[i]; + if (elementAromaticRingIndices.has(e)) elementAromaticRingIndices.get(e)!.push(rI); + else elementAromaticRingIndices.set(e, [rI]); + } + } + // create a graph where vertices are rings, edge if two rings share at least one atom const graph = new IntAdjacencyGraph.UniqueEdgeBuilder(rings.length); for (let rI = 0 as RingIndex, _rI = rings.length; rI < _rI; rI++) { @@ -296,5 +308,5 @@ export function createIndex(rings: ArrayLike<SortedArray<StructureElement.UnitIn ringComponents[ringComponentIndex[rI]].push(rI); } - return { elementRingIndices, ringComponentIndex, ringComponents }; + return { elementRingIndices, elementAromaticRingIndices, ringComponentIndex, ringComponents }; } \ No newline at end of file