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

wip, carbohydrate themes

parent 2357eea1
No related branches found
No related tags found
No related merge requests found
...@@ -15,6 +15,7 @@ import { PolymerGapVisual, DefaultPolymerGapProps } from './visual/polymer-gap-c ...@@ -15,6 +15,7 @@ import { PolymerGapVisual, DefaultPolymerGapProps } from './visual/polymer-gap-c
import { NucleotideBlockVisual, DefaultNucleotideBlockProps } from './visual/nucleotide-block-mesh'; import { NucleotideBlockVisual, DefaultNucleotideBlockProps } from './visual/nucleotide-block-mesh';
import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from './visual/polymer-direction-wedge'; import { PolymerDirectionVisual, DefaultPolymerDirectionProps } from './visual/polymer-direction-wedge';
import { CarbohydrateSymbolVisual } from './visual/carbohydrate-symbol-mesh'; import { CarbohydrateSymbolVisual } from './visual/carbohydrate-symbol-mesh';
import { CarbohydrateLinkVisual } from './visual/carbohydrate-link-cylinder';
export const DefaultCartoonProps = { export const DefaultCartoonProps = {
...DefaultPolymerTraceProps, ...DefaultPolymerTraceProps,
...@@ -29,14 +30,20 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { ...@@ -29,14 +30,20 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
const gapRepr = StructureUnitsRepresentation(PolymerGapVisual) const gapRepr = StructureUnitsRepresentation(PolymerGapVisual)
const blockRepr = StructureUnitsRepresentation(NucleotideBlockVisual) const blockRepr = StructureUnitsRepresentation(NucleotideBlockVisual)
const directionRepr = StructureUnitsRepresentation(PolymerDirectionVisual) const directionRepr = StructureUnitsRepresentation(PolymerDirectionVisual)
const carbohydrateRepr = StructureRepresentation(CarbohydrateSymbolVisual)
// TODO move to own repr
const carbohydrateSymbolRepr = StructureRepresentation(CarbohydrateSymbolVisual)
const carbohydrateLinkRepr = StructureRepresentation(CarbohydrateLinkVisual)
return { return {
get renderObjects() { get renderObjects() {
return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects, ...blockRepr.renderObjects, ...directionRepr.renderObjects, ...carbohydrateRepr.renderObjects ] return [ ...traceRepr.renderObjects, ...gapRepr.renderObjects,
...blockRepr.renderObjects, ...directionRepr.renderObjects,
...carbohydrateSymbolRepr.renderObjects, ...carbohydrateLinkRepr.renderObjects ]
}, },
get props() { get props() {
return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props, ...carbohydrateRepr.props } return { ...traceRepr.props, ...gapRepr.props, ...blockRepr.props,
...carbohydrateSymbolRepr.props, ...carbohydrateLinkRepr.props }
}, },
create: (structure: Structure, props: CartoonProps = {} as CartoonProps) => { create: (structure: Structure, props: CartoonProps = {} as CartoonProps) => {
const p = Object.assign({}, DefaultCartoonProps, props) const p = Object.assign({}, DefaultCartoonProps, props)
...@@ -45,7 +52,8 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { ...@@ -45,7 +52,8 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
await gapRepr.create(structure, p).runInContext(ctx) await gapRepr.create(structure, p).runInContext(ctx)
await blockRepr.create(structure, p).runInContext(ctx) await blockRepr.create(structure, p).runInContext(ctx)
await directionRepr.create(structure, p).runInContext(ctx) await directionRepr.create(structure, p).runInContext(ctx)
await carbohydrateRepr.create(structure, p).runInContext(ctx) await carbohydrateSymbolRepr.create(structure, p).runInContext(ctx)
await carbohydrateLinkRepr.create(structure, p).runInContext(ctx)
}) })
}, },
update: (props: CartoonProps) => { update: (props: CartoonProps) => {
...@@ -55,7 +63,8 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { ...@@ -55,7 +63,8 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
await gapRepr.update(p).runInContext(ctx) await gapRepr.update(p).runInContext(ctx)
await blockRepr.update(p).runInContext(ctx) await blockRepr.update(p).runInContext(ctx)
await directionRepr.update(p).runInContext(ctx) await directionRepr.update(p).runInContext(ctx)
await carbohydrateRepr.update(p).runInContext(ctx) await carbohydrateSymbolRepr.update(p).runInContext(ctx)
await carbohydrateLinkRepr.update(p).runInContext(ctx)
}) })
}, },
getLoci: (pickingId: PickingId) => { getLoci: (pickingId: PickingId) => {
...@@ -63,26 +72,30 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> { ...@@ -63,26 +72,30 @@ export function CartoonRepresentation(): StructureRepresentation<CartoonProps> {
const gapLoci = gapRepr.getLoci(pickingId) const gapLoci = gapRepr.getLoci(pickingId)
const blockLoci = blockRepr.getLoci(pickingId) const blockLoci = blockRepr.getLoci(pickingId)
const directionLoci = directionRepr.getLoci(pickingId) const directionLoci = directionRepr.getLoci(pickingId)
const carbohydrateRepr = directionRepr.getLoci(pickingId) const carbohydrateSymbolLoci = carbohydrateSymbolRepr.getLoci(pickingId)
const carbohydrateLinkLoci = carbohydrateLinkRepr.getLoci(pickingId)
return !isEmptyLoci(traceLoci) ? traceLoci return !isEmptyLoci(traceLoci) ? traceLoci
: !isEmptyLoci(gapLoci) ? gapLoci : !isEmptyLoci(gapLoci) ? gapLoci
: !isEmptyLoci(blockLoci) ? blockLoci : !isEmptyLoci(blockLoci) ? blockLoci
: !isEmptyLoci(directionLoci) ? directionLoci : !isEmptyLoci(directionLoci) ? directionLoci
: carbohydrateRepr : !isEmptyLoci(carbohydrateSymbolLoci) ? carbohydrateSymbolLoci
: carbohydrateLinkLoci
}, },
mark: (loci: Loci, action: MarkerAction) => { mark: (loci: Loci, action: MarkerAction) => {
traceRepr.mark(loci, action) traceRepr.mark(loci, action)
gapRepr.mark(loci, action) gapRepr.mark(loci, action)
blockRepr.mark(loci, action) blockRepr.mark(loci, action)
directionRepr.mark(loci, action) directionRepr.mark(loci, action)
carbohydrateRepr.mark(loci, action) carbohydrateSymbolRepr.mark(loci, action)
carbohydrateLinkRepr.mark(loci, action)
}, },
destroy() { destroy() {
traceRepr.destroy() traceRepr.destroy()
gapRepr.destroy() gapRepr.destroy()
blockRepr.destroy() blockRepr.destroy()
directionRepr.destroy() directionRepr.destroy()
carbohydrateRepr.destroy() carbohydrateSymbolRepr.destroy()
carbohydrateLinkRepr.destroy()
} }
} }
} }
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from 'mol-util/value-cell'
import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
import { Unit, Structure, Link, StructureElement } from 'mol-model/structure';
import { DefaultStructureProps, StructureVisual } from '..';
import { RuntimeContext } from 'mol-task'
import { createIdentityTransform, createColors } from './util/common';
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 { 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';
// 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)
// }
// }
async function createCarbohydrateLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
const { links, elements } = structure.carbohydrates
const builderProps = {
linkCount: links.length,
referencePosition: (edgeIndex: number) => null,
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
const l = links[edgeIndex]
Vec3.copy(posA, elements[l.carbohydrateIndexA].geometry.center)
Vec3.copy(posB, elements[l.carbohydrateIndexB].geometry.center)
},
order: (edgeIndex: number) => 1,
flags: (edgeIndex: number) => 0
}
return createLinkCylinderMesh(ctx, builderProps, props, mesh)
}
export const DefaultCarbohydrateLinkProps = {
...DefaultMeshProps,
...DefaultStructureProps,
...DefaultLinkCylinderProps,
sizeTheme: { name: 'physical', factor: 1 } as SizeTheme,
detail: 0,
unitKinds: [ Unit.Kind.Atomic, Unit.Kind.Spheres ] as Unit.Kind[]
}
export type CarbohydrateLinkProps = Partial<typeof DefaultCarbohydrateLinkProps>
export function CarbohydrateLinkVisual(): StructureVisual<CarbohydrateLinkProps> {
let renderObject: MeshRenderObject
let currentProps: typeof DefaultCarbohydrateLinkProps
let mesh: Mesh
let currentStructure: Structure
return {
get renderObject () { return renderObject },
async create(ctx: RuntimeContext, structure: Structure, props: CarbohydrateLinkProps = {}) {
currentProps = Object.assign({}, DefaultCarbohydrateLinkProps, props)
currentStructure = structure
const { colorTheme } = { ...DefaultCarbohydrateLinkProps, ...props }
const instanceCount = 1
const elementCount = currentStructure.elementCount
mesh = await createCarbohydrateLinkCylinderMesh(ctx, currentStructure, currentProps, mesh)
// console.log(mesh)
const transforms = createIdentityTransform()
const color = createValueColor(0x119911)//createColors(colorTheme)
const marker = createMarkers(instanceCount * elementCount)
const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
const values: MeshValues = {
...getMeshData(mesh),
...color,
...marker,
aTransform: transforms,
elements: mesh.indexBuffer,
...createMeshValues(currentProps, counts),
aColor: ValueCell.create(new Float32Array(mesh.vertexCount * 3))
}
const state = createRenderableState(currentProps)
renderObject = createMeshRenderObject(values, state)
},
async update(ctx: RuntimeContext, props: CarbohydrateLinkProps) {
const newProps = Object.assign({}, currentProps, props)
if (!renderObject) return false
let updateColor = false
// if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
// updateColor = true
// }
// if (updateColor) {
// createColors(LinkIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
// }
updateMeshValues(renderObject.values, newProps)
updateRenderableState(renderObject.state, newProps)
currentProps = newProps
return false
},
getLoci(pickingId: PickingId) {
return getLinkLoci(pickingId, currentStructure, renderObject.id)
},
mark(loci: Loci, action: MarkerAction) {
// TODO
// markLink(loci, action, currentStructure, renderObject.values)
},
destroy() {
// TODO
}
}
}
function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
const { objectId, elementId } = pickingId
if (id === objectId) {
const { links, elements } = structure.carbohydrates
const l = links[elementId]
const carbA = elements[l.carbohydrateIndexA]
const carbB = elements[l.carbohydrateIndexB]
const indexA = OrderedSet.findPredecessorIndex(carbA.unit.elements, carbA.anomericCarbon)
const indexB = OrderedSet.findPredecessorIndex(carbB.unit.elements, carbB.anomericCarbon)
return Link.Loci([
Link.Location(
carbA.unit, indexA as StructureElement.UnitIndex,
carbB.unit, indexB as StructureElement.UnitIndex
)
])
}
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
...@@ -7,26 +7,27 @@ ...@@ -7,26 +7,27 @@
import { ValueCell } from 'mol-util/value-cell' import { ValueCell } from 'mol-util/value-cell'
import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object' import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
import { Unit, Structure } from 'mol-model/structure'; import { Unit, Structure, StructureElement } from 'mol-model/structure';
import { DefaultStructureProps, StructureVisual } from '..'; import { DefaultStructureProps, StructureVisual } from '..';
import { RuntimeContext } from 'mol-task' import { RuntimeContext } from 'mol-task'
import { createIdentityTransform } from './util/common'; import { createIdentityTransform, createColors } from './util/common';
import { MeshValues } from 'mol-gl/renderable'; import { MeshValues } from 'mol-gl/renderable';
import { getMeshData } from '../../../util/mesh-data'; import { getMeshData } from '../../../util/mesh-data';
import { Mesh } from '../../../shape/mesh'; import { Mesh } from '../../../shape/mesh';
import { PickingId } from '../../../util/picking'; import { PickingId } from '../../../util/picking';
import { createMarkers, MarkerAction } from '../../../util/marker-data'; import { createMarkers, MarkerAction, MarkerData, applyMarkerAction } from '../../../util/marker-data';
import { Loci, EmptyLoci } from 'mol-model/loci'; import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
import { SizeTheme } from '../../../theme'; import { SizeTheme } from '../../../theme';
import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util'; import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
import { MeshBuilder } from '../../../shape/mesh-builder'; import { MeshBuilder } from '../../../shape/mesh-builder';
import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Vec3, Mat4 } from 'mol-math/linear-algebra';
import { createValueColor } from '../../../util/color-data';
import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants'; import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
import { deepEqual } from 'mol-util';
import { LocationIterator } from './util/location-iterator';
import { OrderedSet } from 'mol-data/int';
const t = Mat4.identity() const t = Mat4.identity()
const sVec = Vec3.zero() const sVec = Vec3.zero()
const p = Vec3.zero()
const pd = Vec3.zero() const pd = Vec3.zero()
async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, mesh?: Mesh) { async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Structure, mesh?: Mesh) {
...@@ -34,89 +35,74 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru ...@@ -34,89 +35,74 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru
const carbohydrates = structure.carbohydrates const carbohydrates = structure.carbohydrates
function centerAlign(center: Vec3, normal: Vec3, direction: Vec3) {
Vec3.add(pd, center, direction)
Mat4.targetTo(t, center, pd, normal)
Mat4.setTranslation(t, center)
}
const side = 1.75 * 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4) const side = 1.75 * 2 * 0.806; // 0.806 == Math.cos(Math.PI / 4)
const radius = 1.75 const radius = 1.75
const linkParams = { radiusTop: 0.4, radiusBottom: 0.4 }
for (let i = 0, il = carbohydrates.elements.length; i < il; ++i) { 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) const shapeType = getSaccharideShape(c.component.type)
const { center, normal, direction } = c.geometry
Vec3.add(pd, center, direction)
Mat4.targetTo(t, center, pd, normal)
Mat4.setTranslation(t, center)
builder.setId(i)
switch (shapeType) { switch (shapeType) {
case SaccharideShapes.FilledSphere: case SaccharideShapes.FilledSphere:
builder.addSphere(cGeo.center, radius, 2) builder.addSphere(center, radius, 2)
break; break;
case SaccharideShapes.FilledCube: case SaccharideShapes.FilledCube:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.scaleUniformly(t, t, side) Mat4.scaleUniformly(t, t, side)
builder.addBox(t) builder.addBox(t)
break; break;
case SaccharideShapes.CrossedCube: case SaccharideShapes.CrossedCube:
// TODO split // TODO split
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.scaleUniformly(t, t, side) Mat4.scaleUniformly(t, t, side)
builder.addBox(t) builder.addBox(t)
break; break;
case SaccharideShapes.FilledCone: case SaccharideShapes.FilledCone:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.scaleUniformly(t, t, side * 1.2) Mat4.scaleUniformly(t, t, side * 1.2)
builder.addOctagonalPyramid(t) builder.addOctagonalPyramid(t)
break break
case SaccharideShapes.DevidedCone: case SaccharideShapes.DevidedCone:
// TODO split // TODO split
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.scaleUniformly(t, t, side * 1.2) Mat4.scaleUniformly(t, t, side * 1.2)
builder.addOctagonalPyramid(t) builder.addOctagonalPyramid(t)
break break
case SaccharideShapes.FlatBox: case SaccharideShapes.FlatBox:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.mul(t, t, Mat4.rotZY90) Mat4.mul(t, t, Mat4.rotZY90)
Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2)) Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2))
builder.addBox(t) builder.addBox(t)
break break
case SaccharideShapes.FilledStar: case SaccharideShapes.FilledStar:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.mul(t, t, Mat4.rotZY90) Mat4.mul(t, t, Mat4.rotZY90)
builder.addStar(t, { outerRadius: side, innerRadius: side / 2, thickness: side / 2, pointCount: 5 }) builder.addStar(t, { outerRadius: side, innerRadius: side / 2, thickness: side / 2, pointCount: 5 })
break break
case SaccharideShapes.FilledDiamond: case SaccharideShapes.FilledDiamond:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.mul(t, t, Mat4.rotZY90) Mat4.mul(t, t, Mat4.rotZY90)
Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4))
builder.addOctahedron(t) builder.addOctahedron(t)
break break
case SaccharideShapes.DividedDiamond: case SaccharideShapes.DividedDiamond:
// TODO split // TODO split
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.mul(t, t, Mat4.rotZY90) Mat4.mul(t, t, Mat4.rotZY90)
Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4)) Mat4.scale(t, t, Vec3.set(sVec, side * 1.4, side * 1.4, side * 1.4))
builder.addOctahedron(t) builder.addOctahedron(t)
break break
case SaccharideShapes.FlatDiamond: case SaccharideShapes.FlatDiamond:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.mul(t, t, Mat4.rotZY90) Mat4.mul(t, t, Mat4.rotZY90)
Mat4.scale(t, t, Vec3.set(sVec, side, side / 2, side / 2)) Mat4.scale(t, t, Vec3.set(sVec, side, side / 2, side / 2))
builder.addDiamondPrism(t) builder.addDiamondPrism(t)
break break
case SaccharideShapes.Pentagon: case SaccharideShapes.Pentagon:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.mul(t, t, Mat4.rotZY90) Mat4.mul(t, t, Mat4.rotZY90)
Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2)) Mat4.scale(t, t, Vec3.set(sVec, side, side, side / 2))
builder.addPentagonalPrism(t) builder.addPentagonalPrism(t)
break break
case SaccharideShapes.FlatHexagon: case SaccharideShapes.FlatHexagon:
default: default:
centerAlign(cGeo.center, cGeo.normal, cGeo.direction)
Mat4.mul(t, t, Mat4.rotZYZ90) Mat4.mul(t, t, Mat4.rotZYZ90)
Mat4.scale(t, t, Vec3.set(sVec, side / 1.5, side , side / 2)) Mat4.scale(t, t, Vec3.set(sVec, side / 1.5, side , side / 2))
builder.addHexagonalPrism(t) builder.addHexagonalPrism(t)
...@@ -124,24 +110,6 @@ async function createCarbohydrateSymbolMesh(ctx: RuntimeContext, structure: Stru ...@@ -124,24 +110,6 @@ 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].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].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)
}
}
return builder.getMesh() return builder.getMesh()
} }
...@@ -166,14 +134,14 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr ...@@ -166,14 +134,14 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr
currentProps = Object.assign({}, DefaultCarbohydrateSymbolProps, props) currentProps = Object.assign({}, DefaultCarbohydrateSymbolProps, props)
currentStructure = structure currentStructure = structure
const { colorTheme } = { ...DefaultCarbohydrateSymbolProps, ...props }
const instanceCount = 1 const instanceCount = 1
const elementCount = currentStructure.elementCount const elementCount = currentStructure.elementCount
mesh = await createCarbohydrateSymbolMesh(ctx, currentStructure, mesh) mesh = await createCarbohydrateSymbolMesh(ctx, currentStructure, mesh)
// console.log(mesh)
const transforms = createIdentityTransform() const transforms = createIdentityTransform()
const color = createValueColor(0x999911) // TODO const color = createColors(createCarbohydrateIterator(structure), colorTheme)
const marker = createMarkers(instanceCount * elementCount) const marker = createMarkers(instanceCount * elementCount)
const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount } const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
...@@ -196,19 +164,87 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr ...@@ -196,19 +164,87 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr
if (!renderObject) return false if (!renderObject) return false
let updateColor = false
if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
updateColor = true
}
if (updateColor) {
createColors(createCarbohydrateIterator(currentStructure), newProps.colorTheme, renderObject.values)
}
updateMeshValues(renderObject.values, newProps) updateMeshValues(renderObject.values, newProps)
updateRenderableState(renderObject.state, newProps) updateRenderableState(renderObject.state, newProps)
currentProps = newProps
return false return false
}, },
getLoci(pickingId: PickingId) { getLoci(pickingId: PickingId) {
return EmptyLoci return getCarbohydrateLoci(pickingId, currentStructure, renderObject.id)
}, },
mark(loci: Loci, action: MarkerAction) { mark(loci: Loci, action: MarkerAction) {
// TODO markCarbohydrate(loci, action, currentStructure, renderObject.values)
}, },
destroy() { destroy() {
// TODO // TODO
} }
} }
} }
function createCarbohydrateIterator(structure: Structure): LocationIterator {
const carbs = structure.carbohydrates.elements
const elementCount = carbs.length
const instanceCount = 1
const location = StructureElement.create()
const getLocation = (elementIndex: number, instanceIndex: number) => {
const carb = carbs[elementIndex]
location.unit = carb.unit
location.element = carb.anomericCarbon
return location
}
return LocationIterator(elementCount, instanceCount, getLocation)
}
function getCarbohydrateLoci(pickingId: PickingId, structure: Structure, id: number) {
const { objectId, elementId } = pickingId
if (id === objectId) {
const carb = structure.carbohydrates.elements[elementId]
const { unit } = carb
const index = OrderedSet.findPredecessorIndex(unit.elements, carb.anomericCarbon)
const indices = OrderedSet.ofSingleton(index as StructureElement.UnitIndex)
return StructureElement.Loci([{ unit, indices }])
}
return EmptyLoci
}
function markCarbohydrate(loci: Loci, action: MarkerAction, structure: Structure, values: MarkerData) {
const tMarker = values.tMarker
const { byUnitAndElement } = 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 (StructureElement.isLoci(loci)) {
for (const e of loci.elements) {
OrderedSet.forEach(e.indices, index => {
const idx = byUnitAndElement(e.unit, e.unit.elements[index])
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
...@@ -105,7 +105,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?: ...@@ -105,7 +105,7 @@ async function createNucleotideBlockMesh(ctx: RuntimeContext, unit: Unit, mesh?:
} }
if (i % 10000 === 0 && ctx.shouldUpdate) { if (i % 10000 === 0 && ctx.shouldUpdate) {
await ctx.update({ message: 'Gap mesh', current: i }); await ctx.update({ message: 'Nucleotide block mesh', current: i });
} }
++i ++i
} }
......
...@@ -16,6 +16,7 @@ import { ColorTheme, SizeTheme } from '../../../../theme'; ...@@ -16,6 +16,7 @@ import { ColorTheme, SizeTheme } from '../../../../theme';
import { elementIndexColorData, elementSymbolColorData, instanceIndexColorData, chainIdColorData } from '../../../../theme/structure/color'; import { elementIndexColorData, elementSymbolColorData, instanceIndexColorData, chainIdColorData } from '../../../../theme/structure/color';
import { ValueCell, defaults } from 'mol-util'; import { ValueCell, defaults } from 'mol-util';
import { LocationIterator } from './location-iterator'; import { LocationIterator } from './location-iterator';
import { carbohydrateSymbolColorData } from '../../../../theme/structure/color/carbohydrate-symbol';
export function createTransforms({ units }: Unit.SymmetryGroup, transforms?: ValueCell<Float32Array>) { export function createTransforms({ units }: Unit.SymmetryGroup, transforms?: ValueCell<Float32Array>) {
const unitCount = units.length const unitCount = units.length
...@@ -37,6 +38,8 @@ export function createColors(locationIt: LocationIterator, props: ColorTheme, co ...@@ -37,6 +38,8 @@ export function createColors(locationIt: LocationIterator, props: ColorTheme, co
switch (props.name) { switch (props.name) {
case 'atom-index': case 'atom-index':
return elementIndexColorData(locationIt, colorData) return elementIndexColorData(locationIt, colorData)
case 'carbohydrate-symbol':
return carbohydrateSymbolColorData(locationIt, colorData)
case 'chain-id': case 'chain-id':
return chainIdColorData(locationIt, colorData) return chainIdColorData(locationIt, colorData)
case 'element-symbol': case 'element-symbol':
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
*/ */
import { Color } from 'mol-util/color'; import { Color } from 'mol-util/color';
// import { Loci } from 'mol-model/loci';
// import { Structure } from 'mol-model/structure';
export interface UniformColorTheme { export interface UniformColorTheme {
name: 'uniform' name: 'uniform'
...@@ -14,27 +12,15 @@ export interface UniformColorTheme { ...@@ -14,27 +12,15 @@ export interface UniformColorTheme {
} }
export interface ScaleColorTheme { export interface ScaleColorTheme {
name: 'atom-index' | 'chain-id' | 'element-symbol' | 'instance-index' name: 'atom-index' | 'chain-id'| 'instance-index'
domain?: [number, number] domain?: [number, number]
} }
// interface StructureColorProvider { export interface TableColorTheme {
// uniform(): Color name: 'carbohydrate-symbol' | 'element-symbol'
// instance(instanceIdx: number): Color }
// element(elementIdx: number): Color
// elementInstance(elementIdx: number, instanceIdx: number): Color
// lociColor(loci: Loci): Color
// }
// export namespace ColorProvider {
// export function fromLociColor(lociColor: (loci: Loci) => Color) {
// return
// }
// }
export type ColorTheme = UniformColorTheme | ScaleColorTheme export type ColorTheme = UniformColorTheme | ScaleColorTheme | TableColorTheme
export interface UniformSizeTheme { export interface UniformSizeTheme {
name: 'uniform', name: 'uniform',
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Unit, StructureProperties, StructureElement, Link } from 'mol-model/structure';
import { ColorData, createElementColor } from '../../../util/color-data';
import { ColorScale, Color } from 'mol-util/color';
import { LocationIterator, LocationValue } from '../../../representation/structure/visual/util/location-iterator';
function getAsymId(unit: Unit): StructureElement.Property<string> {
switch (unit.kind) {
case Unit.Kind.Atomic:
return StructureProperties.chain.label_asym_id
case Unit.Kind.Spheres:
case Unit.Kind.Gaussians:
return StructureProperties.coarse.asym_id
}
}
export function carbohydrateSymbolColorData(locationIt: LocationIterator, colorData?: ColorData) {
const l = StructureElement.create()
function colorFn(locationValue: LocationValue): Color {
const { location } = locationValue
if (StructureElement.isLocation(location)) {
const map = location.unit.model.properties.asymIdSerialMap
const scale = ColorScale.create({ domain: [ 0, map.size - 1 ] })
const asym_id = getAsymId(location.unit)
return scale.color(map.get(asym_id(location)) || 0)
} else if (Link.isLocation(location)) {
const map = location.aUnit.model.properties.asymIdSerialMap
const scale = ColorScale.create({ domain: [ 0, map.size - 1 ] })
const asym_id = getAsymId(location.aUnit)
l.unit = location.aUnit
l.element = location.aUnit.elements[location.aIndex]
return scale.color(map.get(asym_id(l)) || 0)
}
return 0
}
return createElementColor(locationIt, colorFn, colorData)
}
\ No newline at end of file
...@@ -18,7 +18,7 @@ import StructureElement from '../element'; ...@@ -18,7 +18,7 @@ import StructureElement from '../element';
import Structure from '../structure'; import Structure from '../structure';
import Unit from '../unit'; import Unit from '../unit';
import { SaccharideNameMap, UnknownSaccharideComponent } from './constants'; import { SaccharideNameMap, UnknownSaccharideComponent } from './constants';
import { CarbohydrateElement, CarbohydrateLink, Carbohydrates, CarbohydrateTerminalLink } from './data'; import { CarbohydrateElement, CarbohydrateLink, Carbohydrates, CarbohydrateTerminalLink, PartialCarbohydrateElement } from './data';
import { UnitRings, UnitRing } from '../unit/rings'; import { UnitRings, UnitRing } from '../unit/rings';
import { ElementIndex } from '../../model/indexing'; import { ElementIndex } from '../../model/indexing';
...@@ -49,7 +49,7 @@ function getAnomericCarbon(unit: Unit.Atomic, ringAtoms: ArrayLike<StructureElem ...@@ -49,7 +49,7 @@ function getAnomericCarbon(unit: Unit.Atomic, ringAtoms: ArrayLike<StructureElem
// possibly an anomeric carbon if this is a mono-saccharide without a glycosidic bond // possibly an anomeric carbon if this is a mono-saccharide without a glycosidic bond
indexHasOxygenAndCarbon = ei indexHasOxygenAndCarbon = ei
} else if (label_atom_id.value(ei).startsWith('C1')) { } else if (label_atom_id.value(ei).startsWith('C1')) {
// likely the anomeric carbon as it is name C1 by convention // likely the anomeric carbon as it is named C1 by convention
indexHasC1Name = ei indexHasC1Name = ei
} else { } else {
// use any carbon as a fallback // use any carbon as a fallback
...@@ -122,6 +122,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -122,6 +122,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
const links: CarbohydrateLink[] = [] const links: CarbohydrateLink[] = []
const terminalLinks: CarbohydrateTerminalLink[] = [] const terminalLinks: CarbohydrateTerminalLink[] = []
const elements: CarbohydrateElement[] = [] const elements: CarbohydrateElement[] = []
const partialElements: PartialCarbohydrateElement[] = []
const elementsWithRingMap = new Map<string, number>() const elementsWithRingMap = new Map<string, number>()
...@@ -173,10 +174,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -173,10 +174,7 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
const sugarRings = filterFusedRings(unit.rings, sugarResidueMap.get(residueIndex)); const sugarRings = filterFusedRings(unit.rings, sugarResidueMap.get(residueIndex));
if (!sugarRings || !sugarRings.length) { if (!sugarRings || !sugarRings.length) {
elements.push({ partialElements.push({ unit, residueIndex, component: saccharideComp })
hasRing: false,
unit, residueIndex, component: saccharideComp
})
continue; continue;
} }
...@@ -199,8 +197,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -199,8 +197,8 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
elementsWithRingMap.set(elementKey(residueIndex, unit.id, altId), elementIndex) elementsWithRingMap.set(elementKey(residueIndex, unit.id, altId), elementIndex)
elements.push({ elements.push({
geometry: { center, normal, direction }, geometry: { center, normal, direction },
hasRing: true, component: saccharideComp,
unit, residueIndex, component: saccharideComp unit, residueIndex, anomericCarbon
}) })
} }
...@@ -279,5 +277,20 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates { ...@@ -279,5 +277,20 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
}) })
} }
return { links, terminalLinks, elements } // build lookup map
const map = 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)
}
function key(unit: Unit, anomericCarbon: ElementIndex) {
return `${unit.id}|${anomericCarbon}`
}
function byUnitAndElement(unit: Unit, anomericCarbon: ElementIndex) {
return map.get(key(unit, anomericCarbon))
}
return { links, terminalLinks, elements, partialElements, byUnitAndElement }
} }
\ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import Unit from '../unit'; import Unit from '../unit';
import { Vec3 } from 'mol-math/linear-algebra'; import { Vec3 } from 'mol-math/linear-algebra';
import { ResidueIndex } from '../../model'; import { ResidueIndex, ElementIndex } from '../../model';
import { SaccharideComponent } from './constants'; import { SaccharideComponent } from './constants';
export interface CarbohydrateLink { export interface CarbohydrateLink {
...@@ -23,16 +23,24 @@ export interface CarbohydrateTerminalLink { ...@@ -23,16 +23,24 @@ export interface CarbohydrateTerminalLink {
} }
export interface CarbohydrateElement { export interface CarbohydrateElement {
// geometry is only defined if at least one ring is present. readonly geometry: { readonly center: Vec3, readonly normal: Vec3, readonly direction: Vec3 },
readonly geometry?: { readonly center: Vec3, readonly normal: Vec3, readonly direction: Vec3 }, readonly anomericCarbon: ElementIndex,
readonly hasRing: boolean,
readonly unit: Unit.Atomic, readonly unit: Unit.Atomic,
readonly residueIndex: ResidueIndex, readonly residueIndex: ResidueIndex,
readonly component: SaccharideComponent readonly component: SaccharideComponent,
}
// partial carbohydrate with no ring present
export interface PartialCarbohydrateElement {
readonly unit: Unit.Atomic,
readonly residueIndex: ResidueIndex,
readonly component: SaccharideComponent,
} }
export interface Carbohydrates { export interface Carbohydrates {
links: ReadonlyArray<CarbohydrateLink> links: ReadonlyArray<CarbohydrateLink>
terminalLinks: ReadonlyArray<CarbohydrateTerminalLink> terminalLinks: ReadonlyArray<CarbohydrateTerminalLink>
elements: ReadonlyArray<CarbohydrateElement> elements: ReadonlyArray<CarbohydrateElement>
partialElements: ReadonlyArray<PartialCarbohydrateElement>
byUnitAndElement: (unit: Unit, element: ElementIndex) => number | undefined
} }
\ No newline at end of file
...@@ -110,7 +110,8 @@ export class Stage { ...@@ -110,7 +110,8 @@ export class Stage {
// this.loadMmcifUrl(`../../examples/1cbs_full.bcif`) // this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
// this.loadMmcifUrl(`../../examples/1cbs_updated.cif`) // this.loadMmcifUrl(`../../examples/1cbs_updated.cif`)
// this.loadMmcifUrl(`../../examples/1crn.cif`) // this.loadMmcifUrl(`../../examples/1crn.cif`)
// this.loadPdbid('1zag') // temp // this.loadPdbid('5u0q') // mixed dna/rna in same polymer
// this.loadPdbid('5u0q') // temp
// this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000001.cif`) // ok // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000001.cif`) // ok
// this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000002.cif`) // ok // this.loadMmcifUrl(`../../../test/pdb-dev/PDBDEV_00000002.cif`) // ok
...@@ -154,8 +155,8 @@ export class Stage { ...@@ -154,8 +155,8 @@ export class Stage {
} }
loadPdbid (pdbid: string) { loadPdbid (pdbid: string) {
return this.loadMmcifUrl(`http://www.ebi.ac.uk/pdbe/static/entry/${pdbid}_updated.cif`) // return this.loadMmcifUrl(`http://www.ebi.ac.uk/pdbe/static/entry/${pdbid}_updated.cif`)
// return this.loadMmcifUrl(`https://files.rcsb.org/download/${pdbid}.cif`) return this.loadMmcifUrl(`https://files.rcsb.org/download/${pdbid}.cif`)
} }
dispose () { dispose () {
......
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