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

added carbohydrate terminal link visual

parent 84ccea6b
No related branches found
No related tags found
No related merge requests found
......@@ -262,8 +262,9 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
}
// get carbohydrate links induced by inter-unit bonds
// (e.g. for structures from the PDB archive __before__ carbohydrate remediation)
// get carbohydrate links induced by inter-unit bonds, that is
// terminal links plus inter monosaccharide links for structures from the
// PDB archive __before__ carbohydrate remediation
for (let i = 0, il = structure.units.length; i < il; ++i) {
const unit = structure.units[i]
if (!Unit.isAtomic(unit)) continue
......@@ -309,10 +310,10 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
})
}
return { links, terminalLinks, elements, partialElements, ...buildLookups(elements, links) }
return { links, terminalLinks, elements, partialElements, ...buildLookups(elements, links, terminalLinks) }
}
function buildLookups (elements: CarbohydrateElement[], links: CarbohydrateLink[]) {
function buildLookups (elements: CarbohydrateElement[], links: CarbohydrateLink[], terminalLinks: CarbohydrateTerminalLink[]) {
// element lookup
function elementKey(unit: Unit, anomericCarbon: ElementIndex) {
......@@ -347,6 +348,27 @@ function buildLookups (elements: CarbohydrateElement[], links: CarbohydrateLink[
return linkMap.get(linkKey(unitA, anomericCarbonA, unitB, anomericCarbonB))
}
// terminal link lookup
function terminalLinkKey(unitA: Unit, elementA: ElementIndex, unitB: Unit, elementB: ElementIndex) {
return `${unitA.id}|${elementA}|${unitB.id}|${elementB}`
}
const terminalLinkMap = new Map<string, number>()
for (let i = 0, il = terminalLinks.length; i < il; ++i) {
const { fromCarbohydrate, carbohydrateIndex, elementUnit, elementIndex } = terminalLinks[i]
const { unit, anomericCarbon } = elements[carbohydrateIndex]
if (fromCarbohydrate) {
terminalLinkMap.set(terminalLinkKey(unit, anomericCarbon, elementUnit, elementUnit.elements[elementIndex]), i)
} else {
terminalLinkMap.set(terminalLinkKey(elementUnit, elementUnit.elements[elementIndex], unit, anomericCarbon), i)
}
}
function getTerminalLinkIndex(unitA: Unit, elementA: ElementIndex, unitB: Unit, elementB: ElementIndex) {
return terminalLinkMap.get(terminalLinkKey(unitA, elementA, unitB, elementB))
}
// anomeric carbon lookup
function anomericCarbonKey(unit: Unit, residueIndex: ResidueIndex) {
......@@ -364,5 +386,5 @@ function buildLookups (elements: CarbohydrateElement[], links: CarbohydrateLink[
return anomericCarbonMap.get(anomericCarbonKey(unit, residueIndex))
}
return { getElementIndex, getLinkIndex, getAnomericCarbon }
return { getElementIndex, getLinkIndex, getTerminalLinkIndex, getAnomericCarbon }
}
\ No newline at end of file
......@@ -8,6 +8,7 @@ import Unit from '../unit';
import { Vec3 } from 'mol-math/linear-algebra';
import { ResidueIndex, ElementIndex } from '../../model';
import { SaccharideComponent } from './constants';
import StructureElement from '../element';
export interface CarbohydrateLink {
readonly carbohydrateIndexA: number
......@@ -16,7 +17,7 @@ export interface CarbohydrateLink {
export interface CarbohydrateTerminalLink {
readonly carbohydrateIndex: number
readonly elementIndex: number
readonly elementIndex: StructureElement.UnitIndex
readonly elementUnit: Unit
/** specifies direction of the link */
readonly fromCarbohydrate: boolean
......@@ -31,7 +32,7 @@ export interface CarbohydrateElement {
readonly ringAltId: string,
}
// partial carbohydrate with no ring present
/** partial carbohydrate with no ring present */
export interface PartialCarbohydrateElement {
readonly unit: Unit.Atomic,
readonly residueIndex: ResidueIndex,
......@@ -45,5 +46,6 @@ export interface Carbohydrates {
partialElements: ReadonlyArray<PartialCarbohydrateElement>
getElementIndex: (unit: Unit, anomericCarbon: ElementIndex) => number | undefined
getLinkIndex: (unitA: Unit, anomericCarbonA: ElementIndex, unitB: Unit, anomericCarbonB: ElementIndex) => number | undefined
getTerminalLinkIndex: (unitA: Unit, elementA: ElementIndex, unitB: Unit, elementB: ElementIndex) => number | undefined
getAnomericCarbon: (unit: Unit, residueIndex: ResidueIndex) => ElementIndex | undefined
}
\ No newline at end of file
......@@ -6,6 +6,7 @@
import { CarbohydrateSymbolVisual, CarbohydrateSymbolParams } from '../visual/carbohydrate-symbol-mesh';
import { CarbohydrateLinkVisual, CarbohydrateLinkParams } from '../visual/carbohydrate-link-cylinder';
import { CarbohydrateTerminalLinkParams, CarbohydrateTerminalLinkVisual } from '../visual/carbohydrate-terminal-link-cylinder';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { ComplexRepresentation } from '../complex-representation';
import { StructureRepresentation, StructureRepresentationProvider } from '../representation';
......@@ -17,6 +18,7 @@ import { BuiltInColorThemeOptions, getBuiltInColorThemeParams } from 'mol-theme/
const CarbohydrateVisuals = {
'carbohydrate-symbol': (getParams: RepresentationParamsGetter<Structure, CarbohydrateSymbolParams>) => ComplexRepresentation('Carbohydrate symbol mesh', getParams, CarbohydrateSymbolVisual),
'carbohydrate-link': (getParams: RepresentationParamsGetter<Structure, CarbohydrateLinkParams>) => ComplexRepresentation('Carbohydrate link cylinder', getParams, CarbohydrateLinkVisual),
'carbohydrate-terminal-link': (getParams: RepresentationParamsGetter<Structure, CarbohydrateTerminalLinkParams>) => ComplexRepresentation('Carbohydrate terminal link cylinder', getParams, CarbohydrateTerminalLinkVisual),
}
type CarbohydrateVisualName = keyof typeof CarbohydrateVisuals
const CarbohydrateVisualOptions = Object.keys(CarbohydrateVisuals).map(name => [name, name] as [CarbohydrateVisualName, string])
......@@ -24,8 +26,9 @@ const CarbohydrateVisualOptions = Object.keys(CarbohydrateVisuals).map(name => [
export const CarbohydrateParams = {
...CarbohydrateSymbolParams,
...CarbohydrateLinkParams,
...CarbohydrateTerminalLinkParams,
colorTheme: PD.Mapped('carbohydrate-symbol', BuiltInColorThemeOptions, getBuiltInColorThemeParams),
visuals: PD.MultiSelect<CarbohydrateVisualName>(['carbohydrate-symbol', 'carbohydrate-link'], CarbohydrateVisualOptions),
visuals: PD.MultiSelect<CarbohydrateVisualName>(['carbohydrate-symbol', 'carbohydrate-link', 'carbohydrate-terminal-link'], CarbohydrateVisualOptions),
}
PD.getDefaultValues(CarbohydrateParams).colorTheme.name
export type CarbohydrateParams = typeof CarbohydrateParams
......
......@@ -7,7 +7,7 @@
import { Structure, Link, StructureElement } from 'mol-model/structure';
import { Loci, EmptyLoci } from 'mol-model/loci';
import { Vec3 } from 'mol-math/linear-algebra';
import { createLinkCylinderMesh, LinkCylinderProps, LinkCylinderParams } from './util/link';
import { createLinkCylinderMesh, LinkCylinderParams } from './util/link';
import { OrderedSet, Interval } from 'mol-data/int';
import { ComplexMeshVisual, ComplexVisual } from '../complex-visual';
import { LinkType } from 'mol-model/structure/model/types';
......@@ -21,22 +21,10 @@ import { VisualUpdateState } from '../../util';
import { VisualContext } from 'mol-repr/representation';
import { Theme } from 'mol-theme/theme';
// TODO create seperate visual
// for (let i = 0, il = carbohydrates.terminalLinks.length; i < il; ++i) {
// const tl = carbohydrates.terminalLinks[i]
// 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)
// } else {
// builder.addCylinder(p, center, 0.5, linkParams)
// }
// }
const radiusFactor = 0.3
async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<CarbohydrateLinkParams>, mesh?: Mesh) {
const { links, elements } = structure.carbohydrates
const { linkSizeFactor } = props
const location = StructureElement.create()
const builderProps = {
......@@ -53,7 +41,7 @@ async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure:
const l = links[edgeIndex]
location.unit = elements[l.carbohydrateIndexA].unit
location.element = elements[l.carbohydrateIndexA].anomericCarbon
return theme.size.size(location) * radiusFactor
return theme.size.size(location) * linkSizeFactor
}
}
......@@ -64,6 +52,7 @@ export const CarbohydrateLinkParams = {
...UnitsMeshParams,
...LinkCylinderParams,
detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
linkSizeFactor: PD.Numeric(0.3, { min: 0, max: 3, step: 0.01 }),
}
export type CarbohydrateLinkParams = typeof CarbohydrateLinkParams
......
......@@ -30,8 +30,7 @@ const t = Mat4.identity()
const sVec = Vec3.zero()
const pd = Vec3.zero()
const sideFactor = 1.75 * 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4)
const radiusFactor = 1.75
const SideFactor = 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4)
const box = Box()
const perforatedBox = PerforatedBox()
......@@ -47,7 +46,7 @@ const hexagonalPrism = HexagonalPrism()
async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<CarbohydrateSymbolParams>, mesh?: Mesh) {
const builder = MeshBuilder.create(256, 128, mesh)
const { detail } = props
const { detail, sizeFactor } = props
const carbohydrates = structure.carbohydrates
const n = carbohydrates.elements.length
......@@ -60,8 +59,8 @@ async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Struc
l.unit = c.unit
l.element = c.unit.elements[c.anomericCarbon]
const size = theme.size.size(l)
const radius = size * radiusFactor
const side = size * sideFactor
const radius = size * sizeFactor
const side = size * sizeFactor * SideFactor
const { center, normal, direction } = c.geometry
Vec3.add(pd, center, direction)
......@@ -148,6 +147,7 @@ async function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Struc
export const CarbohydrateSymbolParams = {
...ComplexMeshParams,
detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
sizeFactor: PD.Numeric(1.75, { min: 0, max: 10, step: 0.01 }),
}
export type CarbohydrateSymbolParams = typeof CarbohydrateSymbolParams
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Structure, Link, StructureElement } from 'mol-model/structure';
import { Loci, EmptyLoci } from 'mol-model/loci';
import { Vec3 } from 'mol-math/linear-algebra';
import { createLinkCylinderMesh, LinkCylinderParams } from './util/link';
import { OrderedSet, Interval } from 'mol-data/int';
import { ComplexMeshVisual, ComplexVisual } from '../complex-visual';
import { LinkType } from 'mol-model/structure/model/types';
import { BitFlags } from 'mol-util';
import { UnitsMeshParams } from '../units-visual';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { Mesh } from 'mol-geo/geometry/mesh/mesh';
import { LocationIterator } from 'mol-geo/util/location-iterator';
import { PickingId } from 'mol-geo/geometry/picking';
import { VisualUpdateState } from '../../util';
import { VisualContext } from 'mol-repr/representation';
import { Theme } from 'mol-theme/theme';
async function createCarbohydrateTerminalLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<CarbohydrateTerminalLinkParams>, mesh?: Mesh) {
const { terminalLinks, elements } = structure.carbohydrates
const { linkSizeFactor } = props
const location = StructureElement.create()
const builderProps = {
linkCount: terminalLinks.length,
referencePosition: (edgeIndex: number) => null,
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
const l = terminalLinks[edgeIndex]
if (l.fromCarbohydrate) {
Vec3.copy(posA, elements[l.carbohydrateIndex].geometry.center)
l.elementUnit.conformation.position(l.elementIndex, posB)
} else {
l.elementUnit.conformation.position(l.elementIndex, posA)
Vec3.copy(posB, elements[l.carbohydrateIndex].geometry.center)
}
},
order: (edgeIndex: number) => 1,
flags: (edgeIndex: number) => BitFlags.create(LinkType.Flag.None),
radius: (edgeIndex: number) => {
const l = terminalLinks[edgeIndex]
if (l.fromCarbohydrate) {
location.unit = elements[l.carbohydrateIndex].unit
location.element = elements[l.carbohydrateIndex].anomericCarbon
} else {
location.unit = l.elementUnit
location.element = l.elementUnit.elements[l.elementIndex]
}
return theme.size.size(location) * linkSizeFactor
}
}
return createLinkCylinderMesh(ctx, builderProps, props, mesh)
}
export const CarbohydrateTerminalLinkParams = {
...UnitsMeshParams,
...LinkCylinderParams,
detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
linkSizeFactor: PD.Numeric(0.3, { min: 0, max: 3, step: 0.01 }),
}
export type CarbohydrateTerminalLinkParams = typeof CarbohydrateTerminalLinkParams
export function CarbohydrateTerminalLinkVisual(): ComplexVisual<CarbohydrateTerminalLinkParams> {
return ComplexMeshVisual<CarbohydrateTerminalLinkParams>({
defaultProps: PD.getDefaultValues(CarbohydrateTerminalLinkParams),
createGeometry: createCarbohydrateTerminalLinkCylinderMesh,
createLocationIterator: CarbohydrateTerminalLinkIterator,
getLoci: getTerminalLinkLoci,
mark: markTerminalLink,
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CarbohydrateTerminalLinkParams>, currentProps: PD.Values<CarbohydrateTerminalLinkParams>) => {
state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
}
})
}
function CarbohydrateTerminalLinkIterator(structure: Structure): LocationIterator {
const { elements, terminalLinks } = structure.carbohydrates
const groupCount = terminalLinks.length
const instanceCount = 1
const location = Link.Location()
const getLocation = (groupIndex: number) => {
const terminalLink = terminalLinks[groupIndex]
const carb = elements[terminalLink.carbohydrateIndex]
const indexCarb = OrderedSet.indexOf(carb.unit.elements, carb.anomericCarbon)
if (terminalLink.fromCarbohydrate) {
location.aUnit = carb.unit
location.aIndex = indexCarb as StructureElement.UnitIndex
location.bUnit = terminalLink.elementUnit
location.bIndex = terminalLink.elementIndex
} else {
location.aUnit = terminalLink.elementUnit
location.aIndex = terminalLink.elementIndex
location.bUnit = carb.unit
location.bIndex = indexCarb as StructureElement.UnitIndex
}
return location
}
return LocationIterator(groupCount, instanceCount, getLocation, true)
}
function getTerminalLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
const { objectId, groupId } = pickingId
if (id === objectId) {
const { terminalLinks, elements } = structure.carbohydrates
const l = terminalLinks[groupId]
const carb = elements[l.carbohydrateIndex]
const carbIndex = OrderedSet.indexOf(carb.unit.elements, carb.anomericCarbon)
if (l.fromCarbohydrate) {
return Link.Loci(structure, [
Link.Location(
carb.unit, carbIndex as StructureElement.UnitIndex,
l.elementUnit, l.elementIndex
)
])
} else {
return Link.Loci(structure, [
Link.Location(
l.elementUnit, l.elementIndex,
carb.unit, carbIndex as StructureElement.UnitIndex
)
])
}
}
return EmptyLoci
}
function markTerminalLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
const { getTerminalLinkIndex } = structure.carbohydrates
let changed = false
if (Link.isLoci(loci)) {
for (const l of loci.links) {
const idx = getTerminalLinkIndex(l.aUnit, l.aUnit.elements[l.aIndex], l.bUnit, l.bUnit.elements[l.bIndex])
if (idx !== undefined) {
if (apply(Interval.ofSingleton(idx))) changed = true
}
}
}
return changed
}
\ No newline at end of file
......@@ -7,7 +7,7 @@
import { Link, Structure, StructureElement } from 'mol-model/structure';
import { ComplexVisual } from '../representation';
import { VisualUpdateState } from '../../util';
import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
import { createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
import { Vec3 } from 'mol-math/linear-algebra';
import { Loci, EmptyLoci } from 'mol-model/loci';
import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual';
......@@ -19,9 +19,10 @@ import { PickingId } from 'mol-geo/geometry/picking';
import { VisualContext } from 'mol-repr/representation';
import { Theme } from 'mol-theme/theme';
async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitLinkParams>, mesh?: Mesh) {
const links = structure.links
const { bondCount, bonds } = links
const { sizeFactor } = props
if (!bondCount) return Mesh.createEmpty(mesh)
......@@ -42,7 +43,7 @@ async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: St
const b = bonds[edgeIndex]
location.unit = b.unitA
location.element = b.unitA.elements[b.indexA]
return theme.size.size(location)
return theme.size.size(location) * sizeFactor
}
}
......@@ -52,6 +53,7 @@ async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: St
export const InterUnitLinkParams = {
...ComplexMeshParams,
...LinkCylinderParams,
sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
}
export type InterUnitLinkParams = typeof InterUnitLinkParams
......
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