diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts index 83be8d6a34304e4b244bfc558222fc7e9a1ca4c1..49676477f3217edfaf6f8897a03f80e2927481a0 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-link-cylinder.ts @@ -15,14 +15,13 @@ import { MeshValues } from 'mol-gl/renderable'; import { getMeshData } from '../../../util/mesh-data'; import { Mesh } from '../../../shape/mesh'; import { PickingId } from '../../../util/picking'; -import { createMarkers, MarkerAction, MarkerData } from '../../../util/marker-data'; -import { Loci, EmptyLoci } from 'mol-model/loci'; +import { createMarkers, MarkerAction, MarkerData, applyMarkerAction } from '../../../util/marker-data'; +import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci'; import { SizeTheme } from '../../../theme'; import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util'; import { Vec3 } from 'mol-math/linear-algebra'; import { deepEqual } from 'mol-util'; import { LocationIterator } from './util/location-iterator'; -import { createValueColor } from '../../../util/color-data'; import { createLinkCylinderMesh, DefaultLinkCylinderProps, LinkCylinderProps } from './util/link'; import { OrderedSet } from 'mol-data/int'; @@ -86,7 +85,7 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> // console.log(mesh) const transforms = createIdentityTransform() - const color = createValueColor(0x119911)//createColors(colorTheme) + const color = createColors(createCarbohydrateLinkIterator(structure), colorTheme) const marker = createMarkers(instanceCount * elementCount) const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount } @@ -97,8 +96,7 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> ...marker, aTransform: transforms, elements: mesh.indexBuffer, - ...createMeshValues(currentProps, counts), - aColor: ValueCell.create(new Float32Array(mesh.vertexCount * 3)) + ...createMeshValues(currentProps, counts) } const state = createRenderableState(currentProps) @@ -111,13 +109,13 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> let updateColor = false - // if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) { - // updateColor = true - // } + if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) { + updateColor = true + } - // if (updateColor) { - // createColors(LinkIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values) - // } + if (updateColor) { + createColors(createCarbohydrateLinkIterator(currentStructure), newProps.colorTheme, renderObject.values) + } updateMeshValues(renderObject.values, newProps) updateRenderableState(renderObject.state, newProps) @@ -129,8 +127,7 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> return getLinkLoci(pickingId, currentStructure, renderObject.id) }, mark(loci: Loci, action: MarkerAction) { - // TODO - // markLink(loci, action, currentStructure, renderObject.values) + markLink(loci, action, currentStructure, renderObject.values) }, destroy() { // TODO @@ -138,6 +135,26 @@ export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> } } +function createCarbohydrateLinkIterator(structure: Structure): LocationIterator { + const { elements, links } = structure.carbohydrates + const elementCount = links.length + const instanceCount = 1 + const location = Link.Location() + const getLocation = (elementIndex: number, instanceIndex: number) => { + const link = links[elementIndex] + const carbA = elements[link.carbohydrateIndexA] + const carbB = elements[link.carbohydrateIndexB] + const indexA = OrderedSet.findPredecessorIndex(carbA.unit.elements, carbA.anomericCarbon) + const indexB = OrderedSet.findPredecessorIndex(carbB.unit.elements, carbB.anomericCarbon) + location.aUnit = carbA.unit + location.aIndex = indexA as StructureElement.UnitIndex + location.bUnit = carbB.unit + location.bIndex = indexB as StructureElement.UnitIndex + return location + } + return LocationIterator(elementCount, instanceCount, getLocation) +} + function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) { const { objectId, elementId } = pickingId if (id === objectId) { @@ -157,33 +174,31 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) { return EmptyLoci } -// TODO -// function markLink(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) { -// const tMarker = values.tMarker - -// const links = structure.links -// const elementCount = links.bondCount -// const instanceCount = 1 - -// let changed = false -// const array = tMarker.ref.value.array -// if (isEveryLoci(loci)) { -// applyMarkerAction(array, 0, elementCount * instanceCount, action) -// changed = true -// } else if (Link.isLoci(loci)) { -// for (const b of loci.links) { -// const _idx = structure.links.getBondIndex(b.aIndex, b.aUnit, b.bIndex, b.bUnit) -// if (_idx !== -1) { -// const idx = _idx -// if (applyMarkerAction(array, idx, idx + 1, action) && !changed) { -// changed = true -// } -// } -// } -// } else { -// return -// } -// if (changed) { -// ValueCell.update(tMarker, tMarker.ref.value) -// } -// } \ No newline at end of file +function markLink(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) { + const tMarker = values.tMarker + + const { getLinkIndex } = structure.carbohydrates + const elementCount = structure.carbohydrates.elements.length + + let changed = false + const array = tMarker.ref.value.array + if (isEveryLoci(loci)) { + if (applyMarkerAction(array, 0, elementCount, action)) { + changed = true + } + } else if (Link.isLoci(loci)) { + for (const l of loci.links) { + const idx = getLinkIndex(l.aUnit, l.aUnit.elements[l.aIndex], l.bUnit, l.bUnit.elements[l.bIndex]) + if (idx !== undefined) { + if (applyMarkerAction(array, idx, idx + 1, action) && !changed) { + changed = true + } + } + } + } else { + return + } + if (changed) { + ValueCell.update(tMarker, tMarker.ref.value) + } +} \ No newline at end of file diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts index b0352d0b4986b46413924663351f65f1b5c38807..e51b2049c9b18dfa6a1c4092f0a8a40dc08e1850 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts @@ -141,7 +141,7 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr mesh = await createCarbohydrateSymbolMesh(ctx, currentStructure, mesh) const transforms = createIdentityTransform() - const color = createColors(createCarbohydrateIterator(structure), colorTheme) + const color = createColors(createCarbohydrateElementIterator(structure), colorTheme) const marker = createMarkers(instanceCount * elementCount) const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount } @@ -152,8 +152,7 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr ...marker, aTransform: transforms, elements: mesh.indexBuffer, - ...createMeshValues(currentProps, counts), - aColor: ValueCell.create(new Float32Array(mesh.vertexCount * 3)) + ...createMeshValues(currentProps, counts) } const state = createRenderableState(currentProps) @@ -171,7 +170,7 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr } if (updateColor) { - createColors(createCarbohydrateIterator(currentStructure), newProps.colorTheme, renderObject.values) + createColors(createCarbohydrateElementIterator(currentStructure), newProps.colorTheme, renderObject.values) } updateMeshValues(renderObject.values, newProps) @@ -192,13 +191,13 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr } } -function createCarbohydrateIterator(structure: Structure): LocationIterator { - const carbs = structure.carbohydrates.elements - const elementCount = carbs.length +function createCarbohydrateElementIterator(structure: Structure): LocationIterator { + const carbElements = structure.carbohydrates.elements + const elementCount = carbElements.length const instanceCount = 1 const location = StructureElement.create() const getLocation = (elementIndex: number, instanceIndex: number) => { - const carb = carbs[elementIndex] + const carb = carbElements[elementIndex] location.unit = carb.unit location.element = carb.anomericCarbon return location @@ -221,7 +220,7 @@ function getCarbohydrateLoci(pickingId: PickingId, structure: Structure, id: num function markCarbohydrate(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) { const tMarker = values.tMarker - const { byUnitAndElement } = structure.carbohydrates + const { getElementIndex } = structure.carbohydrates const elementCount = structure.carbohydrates.elements.length let changed = false @@ -233,7 +232,7 @@ function markCarbohydrate(loci: Loci, action: MarkerAction, structure: Structure } else if (StructureElement.isLoci(loci)) { for (const e of loci.elements) { OrderedSet.forEach(e.indices, index => { - const idx = byUnitAndElement(e.unit, e.unit.elements[index]) + const idx = getElementIndex(e.unit, e.unit.elements[index]) if (idx !== undefined) { if (applyMarkerAction(array, idx, idx + 1, action) && !changed) { changed = true diff --git a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts index 585fd7c9979348967fd913ebb9b0dc76f92f07a6..1282c57a0f6b53220379af3bfa7786afaaeddb97 100644 --- a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts +++ b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts @@ -144,13 +144,13 @@ function markLink(loci: Loci, action: MarkerAction, structure: Structure, values const links = structure.links const elementCount = links.bondCount - const instanceCount = 1 let changed = false const array = tMarker.ref.value.array if (isEveryLoci(loci)) { - applyMarkerAction(array, 0, elementCount * instanceCount, action) - changed = true + if (applyMarkerAction(array, 0, elementCount, action)) { + changed = true + } } else if (Link.isLoci(loci)) { for (const b of loci.links) { const _idx = structure.links.getBondIndex(b.aIndex, b.aUnit, b.bIndex, b.bUnit) diff --git a/src/mol-model/structure/structure/carbohydrates/compute.ts b/src/mol-model/structure/structure/carbohydrates/compute.ts index cb1f9a33aa945d78624476e51a55b601038a90ac..ebf52e14c8dee07e214c32fa59b26fe8d5a1481a 100644 --- a/src/mol-model/structure/structure/carbohydrates/compute.ts +++ b/src/mol-model/structure/structure/carbohydrates/compute.ts @@ -126,7 +126,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { const elementsWithRingMap = new Map<string, number>() - function elementKey(residueIndex: number, unitId: number, altId: string) { + function ringElementKey(residueIndex: number, unitId: number, altId: string) { return `${residueIndex}|${unitId}|${altId}` } @@ -194,7 +194,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { const altId = getRingAltId(unit, ringAtoms) const elementIndex = elements.length ringElements.push(elementIndex) - elementsWithRingMap.set(elementKey(residueIndex, unit.id, altId), elementIndex) + elementsWithRingMap.set(ringElementKey(residueIndex, unit.id, altId), elementIndex) elements.push({ geometry: { center, normal, direction }, component: saccharideComp, @@ -227,8 +227,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { } } - function getElementIndex(unit: Unit.Atomic, index: StructureElement.UnitIndex) { - return elementsWithRingMap.get(elementKey(unit.getResidueIndex(index), unit.id, getAltId(unit, index))) + function getRingElementIndex(unit: Unit.Atomic, index: StructureElement.UnitIndex) { + return elementsWithRingMap.get(ringElementKey(unit.getResidueIndex(index), unit.id, getAltId(unit, index))) } // get carbohydrate links induced by inter-unit bonds @@ -241,32 +241,32 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { pairBonds.getBonds(indexA).forEach(bondInfo => { const { unitA, unitB } = pairBonds const indexB = bondInfo.indexB - const elementIndexA = getElementIndex(unitA, indexA) - const elementIndexB = getElementIndex(unitB, indexB) + const ringElementIndexA = getRingElementIndex(unitA, indexA) + const ringElementIndexB = getRingElementIndex(unitB, indexB) - if (elementIndexA !== undefined && elementIndexB !== undefined) { + if (ringElementIndexA !== undefined && ringElementIndexB !== undefined) { const atomIdA = getAtomId(unitA, indexA) if (atomIdA.startsWith('O1') || atomIdA.startsWith('C1')) { - fixLinkDirection(elementIndexA, elementIndexB) + fixLinkDirection(ringElementIndexA, ringElementIndexB) } links.push({ - carbohydrateIndexA: elementIndexA, - carbohydrateIndexB: elementIndexB + carbohydrateIndexA: ringElementIndexA, + carbohydrateIndexB: ringElementIndexB }) - } else if (elementIndexA !== undefined) { + } else if (ringElementIndexA !== undefined) { const atomIdA = getAtomId(unitA, indexA) if (atomIdA.startsWith('O1') || atomIdA.startsWith('C1')) { - fixTerminalLinkDirection(elementIndexA, indexB, unitB) + fixTerminalLinkDirection(ringElementIndexA, indexB, unitB) } terminalLinks.push({ - carbohydrateIndex: elementIndexA, + carbohydrateIndex: ringElementIndexA, elementIndex: indexB, elementUnit: unitB, fromCarbohydrate: true }) - } else if (elementIndexB !== undefined) { + } else if (ringElementIndexB !== undefined) { terminalLinks.push({ - carbohydrateIndex: elementIndexB, + carbohydrateIndex: ringElementIndexB, elementIndex: indexA, elementUnit: unitA, fromCarbohydrate: false @@ -277,20 +277,43 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { }) } - // build lookup map - const map = new Map<string, number>() + return { links, terminalLinks, elements, partialElements, ...buildLookups(elements, links) } +} + +function buildLookups (elements: CarbohydrateElement[], links: CarbohydrateLink[]) { + // element lookup + + function elementKey(unit: Unit, anomericCarbon: ElementIndex) { + return `${unit.id}|${anomericCarbon}` + } + + const elementMap = new Map<string, number>() for (let i = 0, il = elements.length; i < il; ++i) { const { unit, anomericCarbon } = elements[i] - map.set(key(unit, anomericCarbon), i) + elementMap.set(elementKey(unit, anomericCarbon), i) } - function key(unit: Unit, anomericCarbon: ElementIndex) { - return `${unit.id}|${anomericCarbon}` + function getElementIndex(unit: Unit, anomericCarbon: ElementIndex) { + return elementMap.get(elementKey(unit, anomericCarbon)) + } + + // link lookup + + function linkKey(unitA: Unit, anomericCarbonA: ElementIndex, unitB: Unit, anomericCarbonB: ElementIndex) { + return `${unitA.id}|${anomericCarbonA}|${unitB.id}|${anomericCarbonB}` + } + + const linkMap = new Map<string, number>() + for (let i = 0, il = links.length; i < il; ++i) { + const l = links[i] + const { unit: unitA, anomericCarbon: anomericCarbonA } = elements[l.carbohydrateIndexA] + const { unit: unitB, anomericCarbon: anomericCarbonB } = elements[l.carbohydrateIndexB] + linkMap.set(linkKey(unitA, anomericCarbonA, unitB, anomericCarbonB), i) } - function byUnitAndElement(unit: Unit, anomericCarbon: ElementIndex) { - return map.get(key(unit, anomericCarbon)) + function getLinkIndex(unitA: Unit, anomericCarbonA: ElementIndex, unitB: Unit, anomericCarbonB: ElementIndex) { + return linkMap.get(linkKey(unitA, anomericCarbonA, unitB, anomericCarbonB)) } - return { links, terminalLinks, elements, partialElements, byUnitAndElement } + return { getElementIndex, getLinkIndex } } \ No newline at end of file diff --git a/src/mol-model/structure/structure/carbohydrates/data.ts b/src/mol-model/structure/structure/carbohydrates/data.ts index 046748f921acd5d5efc31d5e6d42166d7797d1a4..c69bde439533cd91c36936588394870fde68c83f 100644 --- a/src/mol-model/structure/structure/carbohydrates/data.ts +++ b/src/mol-model/structure/structure/carbohydrates/data.ts @@ -42,5 +42,6 @@ export interface Carbohydrates { terminalLinks: ReadonlyArray<CarbohydrateTerminalLink> elements: ReadonlyArray<CarbohydrateElement> partialElements: ReadonlyArray<PartialCarbohydrateElement> - byUnitAndElement: (unit: Unit, element: ElementIndex) => number | undefined + getElementIndex: (unit: Unit, anomericCarbon: ElementIndex) => number | undefined + getLinkIndex: (unitA: Unit, anomericCarbonA: ElementIndex, unitB: Unit, anomericCarbonB: ElementIndex) => number | undefined } \ No newline at end of file