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 22916064d52e50d22a7aad9364bbc5df710d364f..ad9c384dabeefb3071fa5fbb4ada91bb9d7621cf 100644 --- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts +++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts @@ -47,50 +47,53 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru const linkParams = { radiusTop: 0.4, radiusBottom: 0.4 } for (let i = 0, il = carbohydrates.elements.length; i < il; ++i) { - const c = carbohydrates.elements[i] + const c = carbohydrates.elements[i]; + if (!c.hasRing) continue; + + const cGeo = c.geometry! const shapeType = getSaccharideShape(c.component.type) switch (shapeType) { case SaccharideShapes.FilledSphere: - builder.addIcosahedron(c.center, radius, 2) + builder.addIcosahedron(cGeo.center, radius, 2) break; case SaccharideShapes.FilledCube: - centerAlign(c.center, c.normal, c.direction) + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) builder.addBox(t, { width: side, height: side, depth: side }) break; case SaccharideShapes.CrossedCube: // TODO split - centerAlign(c.center, c.normal, c.direction) + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) builder.addBox(t, { width: side, height: side, depth: side }) break; case SaccharideShapes.FilledCone: - Vec3.scaleAndAdd(p1, c.center, c.direction, radius) - Vec3.scaleAndSub(p2, c.center, c.direction, radius) + Vec3.scaleAndAdd(p1, cGeo.center, cGeo.direction, radius) + Vec3.scaleAndSub(p2, cGeo.center, cGeo.direction, radius) builder.addCylinder(p1, p2, 1, coneParams) break case SaccharideShapes.DevidedCone: // TODO split - Vec3.scaleAndAdd(p1, c.center, c.direction, radius) - Vec3.scaleAndSub(p2, c.center, c.direction, radius) + Vec3.scaleAndAdd(p1, cGeo.center, cGeo.direction, radius) + Vec3.scaleAndSub(p2, cGeo.center, cGeo.direction, radius) builder.addCylinder(p1, p2, 1, coneParams) break case SaccharideShapes.FlatBox: - centerAlign(c.center, c.normal, c.direction) + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) builder.addBox(t, { width: side, height: side / 2, depth: side }) break case SaccharideShapes.FilledStar: - centerAlign(c.center, c.normal, c.direction) + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) builder.addStar(t, { outerRadius: side, innerRadius: side / 2, thickness: side / 2, pointCount: 5 }) break case SaccharideShapes.FilledDiamond: case SaccharideShapes.DividedDiamond: case SaccharideShapes.FlatDiamond: case SaccharideShapes.Pentagon: - centerAlign(c.center, c.normal, c.direction) + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) builder.addBox(t, { width: side, height: side, depth: 0.5 }) break case SaccharideShapes.FlatHexagon: default: - centerAlign(c.center, c.normal, c.direction) + centerAlign(cGeo.center, cGeo.normal, cGeo.direction) builder.addBox(t, { width: side, height: side, depth: 0.1 }) break } @@ -98,14 +101,14 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru for (let i = 0, il = carbohydrates.links.length; i < il; ++i) { const l = carbohydrates.links[i] - const centerA = carbohydrates.elements[l.carbohydrateIndexA].center - const centerB = carbohydrates.elements[l.carbohydrateIndexB].center + const centerA = carbohydrates.elements[l.carbohydrateIndexA].geometry!.center + const centerB = carbohydrates.elements[l.carbohydrateIndexB].geometry!.center builder.addCylinder(centerA, centerB, 0.5, linkParams) } for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) { const tl = carbohydrates.terminalLinks[i] - const center = carbohydrates.elements[tl.carbohydrateIndex].center + const center = carbohydrates.elements[tl.carbohydrateIndex].geometry!.center tl.elementUnit.conformation.position(tl.elementUnit.elements[tl.elementIndex], p) if (tl.fromCarbohydrate) { builder.addCylinder(center, p, 0.5, linkParams) diff --git a/src/mol-model/structure/structure/carbohydrates/compute.ts b/src/mol-model/structure/structure/carbohydrates/compute.ts index 5014945536e7048333054e912d3e8201ef410d66..eb441c0d1cce720a116ddeee1b79d0ce797ce86e 100644 --- a/src/mol-model/structure/structure/carbohydrates/compute.ts +++ b/src/mol-model/structure/structure/carbohydrates/compute.ts @@ -90,22 +90,22 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { const terminalLinks: CarbohydrateTerminalLink[] = [] const elements: CarbohydrateElement[] = [] - const elementsMap = new Map<string, number>() + const elementsWithRingMap = new Map<string, number>() function elementKey(residueIndex: number, unitId: number) { return `${residueIndex}|${unitId}` } function fixLinkDirection(iA: number, iB: number) { - Vec3.sub(elements[iA].direction, elements[iB].center, elements[iA].center) - Vec3.normalize(elements[iA].direction, elements[iA].direction) + Vec3.sub(elements[iA].geometry!.direction, elements[iB].geometry!.center, elements[iA].geometry!.center) + Vec3.normalize(elements[iA].geometry!.direction, elements[iA].geometry!.direction) } const tmpV = Vec3.zero() function fixTerminalLinkDirection(iA: number, indexB: number, unitB: Unit.Atomic) { - const pos = unitB.conformation.position - Vec3.sub(elements[iA].direction, pos(unitB.elements[indexB], tmpV), elements[iA].center) - Vec3.normalize(elements[iA].direction, elements[iA].direction) + const pos = unitB.conformation.position, geo = elements[iA].geometry!; + Vec3.sub(geo.direction, pos(unitB.elements[indexB], tmpV), geo.center) + Vec3.normalize(geo.direction, geo.direction) } // get carbohydrate elements and carbohydrate links induced by intra-residue bonds @@ -140,7 +140,10 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { const sugarRings = sugarResidueMap.get(residueIndex); if (!sugarRings || !sugarRings.length) { - console.warn(`No ring found for carbohydrate on residue with index ${residueIndex}, unit ${unit.id}. Residue skipped.`); + elements.push({ + hasRing: false, + unit, residueIndex, component: saccharideComp + }) continue; } @@ -158,8 +161,12 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { const elementIndex = elements.length ringElements.push(elementIndex) - elementsMap.set(elementKey(residueIndex, unit.id), elementIndex) - elements.push({ center, normal, direction, unit, residueIndex, component: saccharideComp }) + elementsWithRingMap.set(elementKey(residueIndex, unit.id), elementIndex) + elements.push({ + geometry: { center, normal, direction, }, + hasRing: true, + unit, residueIndex, component: saccharideComp + }) } // add carbohydrate links induced by intra-residue bonds @@ -195,8 +202,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { pairBonds.getBonds(indexA).forEach(bondInfo => { const { unitA, unitB } = pairBonds const indexB = bondInfo.indexB - const elementIndexA = elementsMap.get(elementKey(getResidueIndex(indexA, unitA), unitA.id)) - const elementIndexB = elementsMap.get(elementKey(getResidueIndex(indexB, unitB), unitB.id)) + const elementIndexA = elementsWithRingMap.get(elementKey(getResidueIndex(indexA, unitA), unitA.id)) + const elementIndexB = elementsWithRingMap.get(elementKey(getResidueIndex(indexB, unitB), unitB.id)) if (elementIndexA !== undefined && elementIndexB !== undefined) { if (getAtomId(unitA, indexA).startsWith('C1')) { diff --git a/src/mol-model/structure/structure/carbohydrates/data.ts b/src/mol-model/structure/structure/carbohydrates/data.ts index c8f22a1fe31c26c5155a26071c1002a52cc39dd8..0c9361042df84c5d713888b20be4424cb96f66b3 100644 --- a/src/mol-model/structure/structure/carbohydrates/data.ts +++ b/src/mol-model/structure/structure/carbohydrates/data.ts @@ -23,11 +23,11 @@ export interface CarbohydrateTerminalLink { } export interface CarbohydrateElement { - readonly center: Vec3, - readonly normal: Vec3, - readonly direction: Vec3, - readonly unit: Unit.Atomic - readonly residueIndex: ResidueIndex + // geometry is only defined if at least one ring is present. + readonly geometry?: { readonly center: Vec3, readonly normal: Vec3, readonly direction: Vec3 }, + readonly hasRing: boolean, + readonly unit: Unit.Atomic, + readonly residueIndex: ResidueIndex, readonly component: SaccharideComponent }