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

improved carbohydrate/anomeric carbon picking/loci

parent 23a7b0f8
No related branches found
No related tags found
No related merge requests found
......@@ -421,16 +421,22 @@ function buildLookups (elements: CarbohydrateElement[], links: CarbohydrateLink[
return `${unit.id}|${residueIndex}`
}
const anomericCarbonMap = new Map<string, ElementIndex>()
const anomericCarbonMap = new Map<string, ElementIndex[]>()
for (let i = 0, il = elements.length; i < il; ++i) {
const { unit, anomericCarbon } = elements[i]
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index[anomericCarbon]
anomericCarbonMap.set(anomericCarbonKey(unit, residueIndex), anomericCarbon)
const k = anomericCarbonKey(unit, residueIndex)
if (anomericCarbonMap.has(k)) {
anomericCarbonMap.get(k)!.push(anomericCarbon)
} else {
anomericCarbonMap.set(k, [anomericCarbon])
}
}
function getAnomericCarbon(unit: Unit, residueIndex: ResidueIndex) {
return anomericCarbonMap.get(anomericCarbonKey(unit, residueIndex))
const EmptyArray: ReadonlyArray<any> = []
function getAnomericCarbons(unit: Unit, residueIndex: ResidueIndex) {
return anomericCarbonMap.get(anomericCarbonKey(unit, residueIndex)) || EmptyArray
}
return { getElementIndex, getLinkIndex, getLinkIndices, getTerminalLinkIndex, getTerminalLinkIndices, getAnomericCarbon }
return { getElementIndex, getLinkIndex, getLinkIndices, getTerminalLinkIndex, getTerminalLinkIndices, getAnomericCarbons }
}
\ No newline at end of file
......@@ -49,7 +49,7 @@ export interface Carbohydrates {
getLinkIndices: (unit: Unit, anomericCarbon: ElementIndex) => ReadonlyArray<number>
getTerminalLinkIndex: (unitA: Unit, elementA: ElementIndex, unitB: Unit, elementB: ElementIndex) => number | undefined
getTerminalLinkIndices: (unit: Unit, element: ElementIndex) => ReadonlyArray<number>
getAnomericCarbon: (unit: Unit, residueIndex: ResidueIndex) => ElementIndex | undefined
getAnomericCarbons: (unit: Unit, residueIndex: ResidueIndex) => ReadonlyArray<ElementIndex>
}
const EmptyArray: ReadonlyArray<any> = []
......@@ -63,5 +63,5 @@ export const EmptyCarbohydrates: Carbohydrates = {
getLinkIndices: () => EmptyArray,
getTerminalLinkIndex: () => undefined,
getTerminalLinkIndices: () => EmptyArray,
getAnomericCarbon: () => undefined,
getAnomericCarbons: () => [],
}
\ No newline at end of file
......@@ -25,7 +25,7 @@ import { OrderedSet, Interval } from 'mol-data/int';
import { EmptyLoci, Loci } from 'mol-model/loci';
import { VisualContext } from 'mol-repr/visual';
import { Theme } from 'mol-theme/theme';
import { getResidueLoci } from './util/common';
import { getAltResidueLoci } from './util/common';
const t = Mat4.identity()
const sVec = Vec3.zero()
......@@ -186,28 +186,28 @@ function getCarbohydrateLoci(pickingId: PickingId, structure: Structure, id: num
const { objectId, groupId } = pickingId
if (id === objectId) {
const carb = structure.carbohydrates.elements[Math.floor(groupId / 2)]
return getResidueLoci(structure, carb.unit, carb.anomericCarbon)
return getAltResidueLoci(structure, carb.unit, carb.anomericCarbon)
}
return EmptyLoci
}
/** For each carbohydrate (usually a monosaccharide) when all its residue's elements are in a loci. */
function eachCarbohydrate(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
const { getElementIndex, getAnomericCarbon } = structure.carbohydrates
const { getElementIndex, getAnomericCarbons } = structure.carbohydrates
let changed = false
if (!StructureElement.isLoci(loci)) return false
if (!Structure.areEquivalent(loci.structure, structure)) return false
for (const e of loci.elements) {
// TODO make more efficient by handling/grouping `e.indices` by residue index
// TODO only call apply when the full alt-residue of the unit is part of `e`
OrderedSet.forEach(e.indices, v => {
const { model, elements } = e.unit
const { index, offsets } = model.atomicHierarchy.residueAtomSegments
const { index } = model.atomicHierarchy.residueAtomSegments
const rI = index[elements[v]]
const unitIndexMin = OrderedSet.findPredecessorIndex(elements, offsets[rI])
const unitIndexMax = OrderedSet.findPredecessorIndex(elements, offsets[rI + 1] - 1)
const unitIndexInterval = Interval.ofRange(unitIndexMin, unitIndexMax)
if (!OrderedSet.isSubset(e.indices, unitIndexInterval)) return
const eI = getAnomericCarbon(e.unit, rI)
if (eI !== undefined) {
const eIndices = getAnomericCarbons(e.unit, rI)
for (let i = 0, il = eIndices.length; i < il; ++i) {
const eI = eIndices[i]
if (!OrderedSet.has(e.indices, OrderedSet.indexOf(elements, eI))) continue
const idx = getElementIndex(e.unit, eI)
if (idx !== undefined) {
if (apply(Interval.ofBounds(idx * 2, idx * 2 + 2))) changed = true
......
......@@ -28,6 +28,33 @@ export function getResidueLoci(structure: Structure, unit: Unit.Atomic, elementI
return EmptyLoci
}
/**
* Return a Loci for the elements of a whole residue the elementIndex belongs to but
* restrict to elements that have the same label_alt_id or none
*/
export function getAltResidueLoci(structure: Structure, unit: Unit.Atomic, elementIndex: ElementIndex): Loci {
const { elements, model } = unit
const { label_alt_id } = model.atomicHierarchy.atoms
const elementAltId = label_alt_id.value(elementIndex)
if (OrderedSet.indexOf(elements, elementIndex) !== -1) {
const { index, offsets } = model.atomicHierarchy.residueAtomSegments
const rI = index[elementIndex]
const _indices: number[] = []
for (let i = offsets[rI], il = offsets[rI + 1]; i < il; ++i) {
const unitIndex = OrderedSet.indexOf(elements, i)
if (unitIndex !== -1) {
const altId = label_alt_id.value(i)
if (elementAltId === altId || altId === '') {
_indices.push(unitIndex)
}
}
}
const indices = OrderedSet.ofSortedArray<StructureElement.UnitIndex>(SortedArray.ofSortedArray(_indices))
return StructureElement.Loci(structure, [{ unit, indices }])
}
return EmptyLoci
}
//
export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) {
......
......@@ -27,13 +27,13 @@ export function CarbohydrateSymbolColorTheme(ctx: ThemeDataContext, props: PD.Va
let color: LocationColor
if (ctx.structure) {
const { elements, getElementIndex, getAnomericCarbon } = ctx.structure.carbohydrates
const { elements, getElementIndex, getAnomericCarbons } = ctx.structure.carbohydrates
const getColor = (unit: Unit, index: ElementIndex) => {
const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index[index]
const anomericCarbon = getAnomericCarbon(unit, residueIndex)
if (anomericCarbon !== undefined) {
const idx = getElementIndex(unit, anomericCarbon)
const anomericCarbons = getAnomericCarbons(unit, residueIndex)
if (anomericCarbons.length > 0) {
const idx = getElementIndex(unit, anomericCarbons[0])
if (idx !== undefined) return elements[idx].component.color
}
return DefaultColor
......
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