|
@@ -11,6 +11,8 @@ import { StructureElement } from '../../../structure';
|
|
|
import Unit from '../../unit';
|
|
|
import { IntraUnitBonds } from '../bonds/data';
|
|
|
import { sortArray } from '../../../../../mol-data/util';
|
|
|
+import { Column } from '../../../../../mol-data/db';
|
|
|
+import { arraySetAdd, arraySetRemove } from '../../../../../mol-util/array';
|
|
|
|
|
|
export function computeRings(unit: Unit.Atomic) {
|
|
|
const size = largestResidue(unit);
|
|
@@ -42,10 +44,14 @@ interface State {
|
|
|
right: Int32Array,
|
|
|
|
|
|
currentColor: number,
|
|
|
+ currentAltLoc: string,
|
|
|
+ hasAltLoc: boolean,
|
|
|
|
|
|
rings: SortedArray<StructureElement.UnitIndex>[],
|
|
|
+ currentRings: SortedArray<StructureElement.UnitIndex>[],
|
|
|
bonds: IntraUnitBonds,
|
|
|
- unit: Unit.Atomic
|
|
|
+ unit: Unit.Atomic,
|
|
|
+ altLoc: Column<string>
|
|
|
}
|
|
|
|
|
|
function State(unit: Unit.Atomic, capacity: number): State {
|
|
@@ -60,9 +66,13 @@ function State(unit: Unit.Atomic, capacity: number): State {
|
|
|
right: new Int32Array(Constants.MaxDepth),
|
|
|
color: new Int32Array(capacity),
|
|
|
currentColor: 0,
|
|
|
+ currentAltLoc: '',
|
|
|
+ hasAltLoc: false,
|
|
|
rings: [],
|
|
|
+ currentRings: [],
|
|
|
unit,
|
|
|
- bonds: unit.bonds
|
|
|
+ bonds: unit.bonds,
|
|
|
+ altLoc: unit.model.atomicHierarchy.atoms.label_alt_id
|
|
|
};
|
|
|
}
|
|
|
|
|
@@ -75,6 +85,8 @@ function resetState(state: State) {
|
|
|
color[i] = 0;
|
|
|
}
|
|
|
state.currentColor = 0;
|
|
|
+ state.currentAltLoc = '';
|
|
|
+ state.hasAltLoc = false;
|
|
|
}
|
|
|
|
|
|
function largestResidue(unit: Unit.Atomic) {
|
|
@@ -95,17 +107,48 @@ function processResidue(state: State, start: number, end: number) {
|
|
|
// no two atom rings
|
|
|
if (state.endVertex - state.startVertex < 3) return;
|
|
|
|
|
|
- resetState(state);
|
|
|
+ state.currentRings = [];
|
|
|
|
|
|
- for (let i = 0; i < state.count; i++) {
|
|
|
- if (visited[i] >= 0) continue;
|
|
|
- findRings(state, i);
|
|
|
+ const { elements } = state.unit;
|
|
|
+ const altLocs: string[] = [];
|
|
|
+ for (let i = state.startVertex; i < state.endVertex; i++) {
|
|
|
+ const altLoc = state.altLoc.value(elements[i]);
|
|
|
+ arraySetAdd(altLocs, altLoc);
|
|
|
+ }
|
|
|
+ arraySetRemove(altLocs, '');
|
|
|
+
|
|
|
+ if (altLocs.length === 0) {
|
|
|
+ resetState(state);
|
|
|
+ for (let i = 0; i < state.count; i++) {
|
|
|
+ if (visited[i] >= 0) continue;
|
|
|
+ findRings(state, i);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (let aI = 0; aI < altLocs.length; aI++) {
|
|
|
+ resetState(state);
|
|
|
+ state.hasAltLoc = true;
|
|
|
+ state.currentAltLoc = altLocs[aI];
|
|
|
+ for (let i = 0; i < state.count; i++) {
|
|
|
+ if (visited[i] >= 0) continue;
|
|
|
+ const altLoc = state.altLoc.value(elements[state.startVertex + i]);
|
|
|
+ if (altLoc && altLoc !== state.currentAltLoc) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ findRings(state, i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0, _i = state.currentRings.length; i < _i; i++) {
|
|
|
+ state.rings.push(state.currentRings[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function addRing(state: State, a: number, b: number) {
|
|
|
// only "monotonous" rings
|
|
|
- if (b < a) return;
|
|
|
+ if (b < a) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
const { pred, color, left, right } = state;
|
|
|
const nc = ++state.currentColor;
|
|
@@ -132,7 +175,9 @@ function addRing(state: State, a: number, b: number) {
|
|
|
current = pred[current];
|
|
|
if (current < 0) break;
|
|
|
}
|
|
|
- if (!found) return;
|
|
|
+ if (!found) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
current = a;
|
|
|
for (let t = 0; t < Constants.MaxDepth; t++) {
|
|
@@ -144,7 +189,9 @@ function addRing(state: State, a: number, b: number) {
|
|
|
|
|
|
const len = leftOffset + rightOffset
|
|
|
// rings must have at least three elements
|
|
|
- if (len < 3) return
|
|
|
+ if (len < 3) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
const ring = new Int32Array(len);
|
|
|
let ringOffset = 0;
|
|
@@ -152,11 +199,34 @@ function addRing(state: State, a: number, b: number) {
|
|
|
for (let t = rightOffset - 1; t >= 0; t--) ring[ringOffset++] = state.startVertex + right[t];
|
|
|
|
|
|
sortArray(ring);
|
|
|
- state.rings.push(SortedArray.ofSortedArray(ring));
|
|
|
+
|
|
|
+ if (state.hasAltLoc) {
|
|
|
+ // we need to check if the ring was already added because alt locs are present.
|
|
|
+
|
|
|
+ for (let rI = 0, _rI = state.currentRings.length; rI < _rI; rI++) {
|
|
|
+ const r = state.currentRings[rI];
|
|
|
+ if (ring[0] !== r[0]) continue;
|
|
|
+ if (ring.length !== r.length) continue;
|
|
|
+
|
|
|
+ let areSame = true;
|
|
|
+ for (let aI = 0, _aI = ring.length; aI < _aI; aI++) {
|
|
|
+ if (ring[aI] !== r[aI]) {
|
|
|
+ areSame = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (areSame) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ state.currentRings.push(SortedArray.ofSortedArray(ring));
|
|
|
}
|
|
|
|
|
|
function findRings(state: State, from: number) {
|
|
|
const { bonds, startVertex, endVertex, visited, queue, pred } = state;
|
|
|
+ const { elements } = state.unit;
|
|
|
const { b: neighbor, edgeProps: { flags: bondFlags }, offset } = bonds;
|
|
|
visited[from] = 1;
|
|
|
queue[0] = from;
|
|
@@ -171,10 +241,19 @@ function findRings(state: State, from: number) {
|
|
|
const b = neighbor[i];
|
|
|
if (b < startVertex || b >= endVertex || !BondType.isCovalent(bondFlags[i])) continue;
|
|
|
|
|
|
+ if (state.hasAltLoc) {
|
|
|
+ const altLoc = state.altLoc.value(elements[b]);
|
|
|
+ if (altLoc && state.currentAltLoc !== altLoc) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const other = b - startVertex;
|
|
|
|
|
|
if (visited[other] > 0) {
|
|
|
- if (pred[other] !== top && pred[top] !== other) addRing(state, top, other);
|
|
|
+ if (pred[other] !== top && pred[top] !== other) {
|
|
|
+ addRing(state, top, other);
|
|
|
+ }
|
|
|
continue;
|
|
|
}
|
|
|
|