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

wip, bond repr

parent a1288e83
No related branches found
No related tags found
No related merge requests found
...@@ -13,7 +13,7 @@ import { View } from '../view'; ...@@ -13,7 +13,7 @@ import { View } from '../view';
import { EntityTreeController } from '../../controller/entity/tree'; import { EntityTreeController } from '../../controller/entity/tree';
import { Controller } from '../../controller/controller'; import { Controller } from '../../controller/controller';
import { AnyEntity, RootEntity } from 'mol-view/state/entity'; import { AnyEntity, RootEntity } from 'mol-view/state/entity';
import { AnyTransform, SpacefillUpdate, UrlToData, DataToCif, FileToData, CifToMmcif, MmcifToModel, ModelToStructure, StructureToSpacefill, MmcifFileToSpacefill, StructureCenter } from 'mol-view/state/transform'; import { AnyTransform, SpacefillUpdate, UrlToData, DataToCif, FileToData, CifToMmcif, MmcifToModel, ModelToStructure, StructureToSpacefill, MmcifFileToSpacefill, StructureCenter, StructureToBond, BondUpdate } from 'mol-view/state/transform';
function getTransforms(entity: AnyEntity): AnyTransform[] { function getTransforms(entity: AnyEntity): AnyTransform[] {
const transforms: AnyTransform[] = [] const transforms: AnyTransform[] = []
...@@ -40,11 +40,14 @@ function getTransforms(entity: AnyEntity): AnyTransform[] { ...@@ -40,11 +40,14 @@ function getTransforms(entity: AnyEntity): AnyTransform[] {
transforms.push(ModelToStructure) transforms.push(ModelToStructure)
break; break;
case 'structure': case 'structure':
transforms.push(StructureToSpacefill, StructureCenter) transforms.push(StructureToSpacefill, StructureToBond, StructureCenter)
break; break;
case 'spacefill': case 'spacefill':
transforms.push(SpacefillUpdate) transforms.push(SpacefillUpdate)
break; break;
case 'bond':
transforms.push(BondUpdate)
break;
} }
return transforms return transforms
} }
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Adapted from LiteMol
* Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { View } from '../view';
import { Controller } from '../../controller/controller';
import { Toggle } from '../controls/common';
import { BondEntity } from 'mol-view/state/entity';
import { BondUpdate } from 'mol-view/state/transform'
import { StateContext } from 'mol-view/state/context';
import { ColorTheme } from 'mol-geo/theme';
import { Color, ColorNames } from 'mol-util/color';
import { Slider } from '../controls/slider';
export const ColorThemeInfo = {
'atom-index': {},
'chain-id': {},
'element-symbol': {},
'instance-index': {},
'uniform': {}
}
export type ColorThemeInfo = keyof typeof ColorThemeInfo
interface BondState {
doubleSided: boolean
flipSided: boolean
flatShaded: boolean
colorTheme: ColorTheme
colorValue: Color
visible: boolean
alpha: number
depthMask: boolean
}
export class Bond extends View<Controller<any>, BondState, { transform: BondUpdate, entity: BondEntity, ctx: StateContext }> {
state = {
doubleSided: true,
flipSided: false,
flatShaded: false,
colorTheme: { name: 'element-symbol' } as ColorTheme,
colorValue: 0x000000,
visible: true,
alpha: 1,
depthMask: true
}
update(state?: Partial<BondState>) {
const { transform, entity, ctx } = this.props
const newState = { ...this.state, ...state }
this.setState(newState)
transform.apply(ctx, entity, newState)
}
render() {
const { transform } = this.props
const colorThemeOptions = Object.keys(ColorThemeInfo).map((name, idx) => {
return <option key={name} value={name}>{name}</option>
})
const colorValueOptions = Object.keys(ColorNames).map((name, idx) => {
return <option key={name} value={(ColorNames as any)[name]}>{name}</option>
})
return <div className='molstar-transformer-wrapper'>
<div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'>
<div className='molstar-panel-header'>
<button
className='molstar-btn molstar-btn-link molstar-panel-expander'
onClick={() => this.update()}
>
<span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span>
</button>
</div>
<div className='molstar-panel-body'>
<div>
<div className='molstar-control-row molstar-options-group'>
<span>Color theme</span>
<div>
<select
className='molstar-form-control'
value={this.state.colorTheme.name}
onChange={(e) => {
const colorThemeName = e.target.value as ColorThemeInfo
if (colorThemeName === 'uniform') {
this.update({
colorTheme: {
name: colorThemeName,
value: this.state.colorValue
}
})
} else {
this.update({
colorTheme: { name: colorThemeName }
})
}
}}
>
{colorThemeOptions}
</select>
</div>
</div>
<div className='molstar-control-row molstar-options-group'>
<span>Color value</span>
<div>
<select
className='molstar-form-control'
value={this.state.colorValue}
onChange={(e) => {
const colorValue = parseInt(e.target.value)
this.update({
colorTheme: {
name: 'uniform',
value: colorValue
},
colorValue
})
}}
>
{colorValueOptions}
</select>
</div>
</div>
<div className='molstar-control-row molstar-options-group'>
<div>
<Toggle
value={this.state.visible}
label='Visibility'
onChange={value => this.update({ visible: value })}
/>
</div>
</div>
<div className='molstar-control-row molstar-options-group'>
<div>
<Toggle
value={this.state.depthMask}
label='Depth write'
onChange={value => this.update({ depthMask: value })}
/>
</div>
</div>
<div className='molstar-control-row molstar-options-group'>
<div>
<Toggle
value={this.state.doubleSided}
label='Double sided'
onChange={value => this.update({ doubleSided: value })}
/>
</div>
</div>
<div className='molstar-control-row molstar-options-group'>
<div>
<Toggle
value={this.state.flipSided}
label='Flip sided'
onChange={value => this.update({ flipSided: value })}
/>
</div>
</div>
<div className='molstar-control-row molstar-options-group'>
<div>
<Toggle
value={this.state.flatShaded}
label='Flat shaded'
onChange={value => this.update({ flatShaded: value })}
/>
</div>
</div>
<div className='molstar-control-row molstar-options-group'>
<div>
<Slider
value={this.state.alpha}
label='Opacity'
min={0}
max={1}
step={0.01}
callOnChangeWhileSliding={true}
onChange={value => this.update({ alpha: value })}
/>
</div>
</div>
</div>
</div>
</div>
</div>;
}
}
\ No newline at end of file
...@@ -14,6 +14,7 @@ import { Controller } from '../../controller/controller'; ...@@ -14,6 +14,7 @@ import { Controller } from '../../controller/controller';
import { TransformListController } from '../../controller/transform/list'; import { TransformListController } from '../../controller/transform/list';
import { AnyTransform } from 'mol-view/state/transform'; import { AnyTransform } from 'mol-view/state/transform';
import { Spacefill } from './spacefill'; import { Spacefill } from './spacefill';
import { Bond } from './bond';
import { AnyEntity } from 'mol-view/state/entity'; import { AnyEntity } from 'mol-view/state/entity';
import { FileLoader } from './file-loader'; import { FileLoader } from './file-loader';
import { ModelToStructure } from './model'; import { ModelToStructure } from './model';
...@@ -29,6 +30,8 @@ function getTransformComponent(controller: TransformListController, entity: AnyE ...@@ -29,6 +30,8 @@ function getTransformComponent(controller: TransformListController, entity: AnyE
return <StructureCenter controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></StructureCenter> return <StructureCenter controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></StructureCenter>
case 'spacefill-update': case 'spacefill-update':
return <Spacefill controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Spacefill> return <Spacefill controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Spacefill>
case 'bond-update':
return <Bond controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Bond>
} }
return <Transform controller={controller} entity={entity} transform={transform}></Transform> return <Transform controller={controller} entity={entity} transform={transform}></Transform>
} }
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { ValueCell } from 'mol-util/value-cell'
import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
import { Unit, Element } from 'mol-model/structure';
import { UnitsRepresentation, DefaultStructureProps } from './index';
import { Task } from 'mol-task'
import { createTransforms, createEmptyFlags } from './utils';
import { fillSerial } from 'mol-gl/renderable/util';
import { RenderableState, MeshValues } from 'mol-gl/renderable';
import { getMeshData } from '../../util/mesh-data';
import { Mesh } from '../../shape/mesh';
import { PickingId } from '../../util/picking';
import { MeshBuilder } from '../../shape/mesh-builder';
import { Vec3, Mat4 } from 'mol-math/linear-algebra';
import { createUniformColor } from '../../util/color-data';
import { defaults } from 'mol-util';
function createBondMesh(unit: Unit, mesh?: Mesh) {
return Task.create('Cylinder mesh', async ctx => {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
const elements = unit.elements;
const { count, offset, neighbor } = unit.bonds;
if (!count) return Mesh.createEmpty(mesh)
// TODO calculate properly
const vertexCount = 32 * count
const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh)
const va = Vec3.zero()
const vb = Vec3.zero()
const vt = Vec3.zero()
const m = Mat4.identity()
const { x, y, z } = unit.conformation
const l = Element.Location()
l.unit = unit
for (let j = 0; j < offset.length - 1; ++j) {
const start = offset[j]
const end = offset[j + 1]
if (end <= start) continue
const aI = elements[j]
va[0] = x(aI)
va[1] = y(aI)
va[2] = z(aI)
for (let _bI = start; _bI < end; ++_bI) {
const bI = elements[neighbor[_bI]]
if (bI > aI) continue
vb[0] = x(bI)
vb[1] = y(bI)
vb[2] = z(bI)
Vec3.scale(vt, Vec3.add(vt, va, vb), 0.5)
Vec3.makeRotation(m, Vec3.create(0, 1, 0), Vec3.sub(vb, vb, va))
Mat4.setTranslation(m, vt)
meshBuilder.setId(j)
meshBuilder.addCylinder(m, { radiusTop: 0.2, radiusBottom: 0.2 })
}
if (j % 10000 === 0 && ctx.shouldUpdate) {
await ctx.update({ message: 'Cylinder mesh', current: j, max: count });
}
}
return meshBuilder.getMesh()
})
}
export const DefaultBondProps = {
...DefaultStructureProps,
flipSided: false,
flatShaded: false,
}
export type BondProps = Partial<typeof DefaultBondProps>
export default function Bond(): UnitsRepresentation<BondProps> {
const renderObjects: RenderObject[] = []
let cylinders: MeshRenderObject
let currentProps: typeof DefaultBondProps
let mesh: Mesh
// let currentGroup: Unit.SymmetryGroup
// let vertexMap: VertexMap
return {
renderObjects,
create(group: Unit.SymmetryGroup, props: BondProps = {}) {
currentProps = Object.assign({}, DefaultBondProps, props)
return Task.create('Bond.create', async ctx => {
renderObjects.length = 0 // clear
// currentGroup = group
mesh = await createBondMesh(group.units[0]).runAsChild(ctx, 'Computing bond mesh')
// console.log(mesh)
// vertexMap = VertexMap.fromMesh(mesh)
await ctx.update('Computing bond transforms');
const transforms = createTransforms(group)
await ctx.update('Computing bond colors');
const color = createUniformColor({ value: 0xFF0000 })
await ctx.update('Computing bond flags');
const flag = createEmptyFlags()
const instanceCount = group.units.length
const values: MeshValues = {
...getMeshData(mesh),
aTransform: transforms,
aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
...color,
...flag,
uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
uInstanceCount: ValueCell.create(instanceCount),
uElementCount: ValueCell.create(group.elements.length),
elements: mesh.indexBuffer,
drawCount: ValueCell.create(mesh.triangleCount * 3),
instanceCount: ValueCell.create(instanceCount),
dDoubleSided: ValueCell.create(defaults(props.doubleSided, true)),
dFlatShaded: ValueCell.create(defaults(props.flatShaded, false)),
dFlipSided: ValueCell.create(defaults(props.flipSided, false)),
}
const state: RenderableState = {
depthMask: defaults(props.depthMask, true),
visible: defaults(props.visible, true)
}
cylinders = createMeshRenderObject(values, state)
renderObjects.push(cylinders)
})
},
update(props: BondProps) {
const newProps = Object.assign({}, currentProps, props)
return Task.create('Bond.update', async ctx => {
if (!cylinders) return false
// TODO
ValueCell.updateIfChanged(cylinders.values.uAlpha, newProps.alpha)
ValueCell.updateIfChanged(cylinders.values.dDoubleSided, newProps.doubleSided)
ValueCell.updateIfChanged(cylinders.values.dFlipSided, newProps.flipSided)
ValueCell.updateIfChanged(cylinders.values.dFlatShaded, newProps.flatShaded)
cylinders.state.visible = newProps.visible
cylinders.state.depthMask = newProps.depthMask
return true
})
},
getLocation(pickingId: PickingId) {
// const { objectId, instanceId, elementId } = pickingId
// if (cylinders.id === objectId) {
// const l = Element.Location()
// l.unit = currentGroup.units[instanceId]
// l.element = currentGroup.elements[elementId]
// return l
// }
return null
}
}
}
...@@ -30,7 +30,7 @@ function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) { ...@@ -30,7 +30,7 @@ function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) {
console.warn('Unsupported unit type') console.warn('Unsupported unit type')
return Task.constant('Empty mesh', Mesh.createEmpty(mesh)) return Task.constant('Empty mesh', Mesh.createEmpty(mesh))
} }
return createSphereMesh(unit, radius, detail, mesh) return createSphereMesh(unit, (l) => radius(l) * 0.3, detail, mesh)
} }
export const DefaultSpacefillProps = { export const DefaultSpacefillProps = {
......
...@@ -13,6 +13,7 @@ import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; ...@@ -13,6 +13,7 @@ import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif';
import { Model, Structure } from 'mol-model/structure'; import { Model, Structure } from 'mol-model/structure';
import { StructureRepresentation } from 'mol-geo/representation/structure'; import { StructureRepresentation } from 'mol-geo/representation/structure';
import { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; import { SpacefillProps } from 'mol-geo/representation/structure/spacefill';
import { BondProps } from 'mol-geo/representation/structure/bond';
const getNextId = idFactory(1) const getNextId = idFactory(1)
...@@ -119,4 +120,11 @@ export namespace SpacefillEntity { ...@@ -119,4 +120,11 @@ export namespace SpacefillEntity {
export function ofRepr(ctx: StateContext, repr: StructureRepresentation<SpacefillProps>): SpacefillEntity { export function ofRepr(ctx: StateContext, repr: StructureRepresentation<SpacefillProps>): SpacefillEntity {
return StateEntity.create(ctx, 'spacefill', repr ) return StateEntity.create(ctx, 'spacefill', repr )
} }
}
export type BondEntity = StateEntity<StructureRepresentation<BondProps>, 'bond'>
export namespace BondEntity {
export function ofRepr(ctx: StateContext, repr: StructureRepresentation<BondProps>): BondEntity {
return StateEntity.create(ctx, 'bond', repr )
}
} }
\ No newline at end of file
...@@ -5,13 +5,14 @@ ...@@ -5,13 +5,14 @@
*/ */
import CIF from 'mol-io/reader/cif' import CIF from 'mol-io/reader/cif'
import { FileEntity, DataEntity, UrlEntity, CifEntity, MmcifEntity, ModelEntity, StructureEntity, SpacefillEntity, AnyEntity, NullEntity } from './entity'; import { FileEntity, DataEntity, UrlEntity, CifEntity, MmcifEntity, ModelEntity, StructureEntity, SpacefillEntity, AnyEntity, NullEntity, BondEntity } from './entity';
import { Model, Structure } from 'mol-model/structure'; import { Model, Structure } from 'mol-model/structure';
import { StateContext } from './context'; import { StateContext } from './context';
import Spacefill, { SpacefillProps } from 'mol-geo/representation/structure/spacefill'; import Spacefill, { SpacefillProps } from 'mol-geo/representation/structure/spacefill';
import { StructureRepresentation } from 'mol-geo/representation/structure'; import { StructureRepresentation } from 'mol-geo/representation/structure';
import StructureSymmetry from 'mol-model/structure/structure/symmetry'; import StructureSymmetry from 'mol-model/structure/structure/symmetry';
import Bond, { BondProps } from 'mol-geo/representation/structure/bond';
type transformer<I extends AnyEntity, O extends AnyEntity, P extends {}> = (ctx: StateContext, inputEntity: I, props?: P) => Promise<O> type transformer<I extends AnyEntity, O extends AnyEntity, P extends {}> = (ctx: StateContext, inputEntity: I, props?: P) => Promise<O>
...@@ -98,30 +99,42 @@ export const StructureToSpacefill: StructureToSpacefill = StateTransform.create( ...@@ -98,30 +99,42 @@ export const StructureToSpacefill: StructureToSpacefill = StateTransform.create(
ctx.viewer.add(spacefillRepr) ctx.viewer.add(spacefillRepr)
ctx.viewer.requestDraw() ctx.viewer.requestDraw()
console.log('stats', ctx.viewer.stats) console.log('stats', ctx.viewer.stats)
// ctx.viewer.input.drag.subscribe(async () => {
// console.log('drag')
// console.time('spacefill update')
// await spacefillRepr.update(props).run(ctx.log)
// console.timeEnd('spacefill update')
// ctx.viewer.add(spacefillRepr)
// ctx.viewer.update()
// ctx.viewer.requestDraw()
// })
return SpacefillEntity.ofRepr(ctx, spacefillRepr) return SpacefillEntity.ofRepr(ctx, spacefillRepr)
}) })
export type StructureToBond = StateTransform<StructureEntity, BondEntity, BondProps>
export const StructureToBond: StructureToBond = StateTransform.create('structure', 'bond', 'structure-to-bond',
async function (ctx: StateContext, structureEntity: StructureEntity, props: BondProps = {}) {
const bondRepr = StructureRepresentation(Bond)
await bondRepr.create(structureEntity.value, props).run(ctx.log)
ctx.viewer.add(bondRepr)
ctx.viewer.requestDraw()
console.log('stats', ctx.viewer.stats)
return BondEntity.ofRepr(ctx, bondRepr)
})
export type SpacefillUpdate = StateTransform<SpacefillEntity, NullEntity, SpacefillProps> export type SpacefillUpdate = StateTransform<SpacefillEntity, NullEntity, SpacefillProps>
export const SpacefillUpdate: SpacefillUpdate = StateTransform.create('spacefill', 'null', 'spacefill-update', export const SpacefillUpdate: SpacefillUpdate = StateTransform.create('spacefill', 'null', 'spacefill-update',
async function (ctx: StateContext, spacefillEntity: SpacefillEntity, props: SpacefillProps = {}) { async function (ctx: StateContext, spacefillEntity: SpacefillEntity, props: SpacefillProps = {}) {
const spacefillRepr = spacefillEntity.value const spacefillRepr = spacefillEntity.value
await spacefillRepr.update(props).run(ctx.log) await spacefillRepr.update(props).run(ctx.log)
ctx.viewer.add(spacefillRepr) ctx.viewer.add(spacefillRepr)
// ctx.viewer.update()
ctx.viewer.requestDraw() ctx.viewer.requestDraw()
console.log('stats', ctx.viewer.stats) console.log('stats', ctx.viewer.stats)
return NullEntity return NullEntity
}) })
export type BondUpdate = StateTransform<BondEntity, NullEntity, BondProps>
export const BondUpdate: BondUpdate = StateTransform.create('bond', 'null', 'bond-update',
async function (ctx: StateContext, bondEntity: BondEntity, props: BondProps = {}) {
const bondRepr = bondEntity.value
await bondRepr.update(props).run(ctx.log)
ctx.viewer.add(bondRepr)
ctx.viewer.requestDraw()
console.log('stats', ctx.viewer.stats)
return NullEntity
})
// composed // composed
export type MmcifUrlToModel = StateTransform<UrlEntity, ModelEntity, {}> export type MmcifUrlToModel = StateTransform<UrlEntity, ModelEntity, {}>
...@@ -150,6 +163,7 @@ export type ModelToSpacefill = StateTransform<ModelEntity, SpacefillEntity, Spac ...@@ -150,6 +163,7 @@ export type ModelToSpacefill = StateTransform<ModelEntity, SpacefillEntity, Spac
export const ModelToSpacefill: ModelToSpacefill = StateTransform.create('model', 'spacefill', 'model-to-spacefill', export const ModelToSpacefill: ModelToSpacefill = StateTransform.create('model', 'spacefill', 'model-to-spacefill',
async function (ctx: StateContext, modelEntity: ModelEntity, props: SpacefillProps = {}) { async function (ctx: StateContext, modelEntity: ModelEntity, props: SpacefillProps = {}) {
const structureEntity = await ModelToStructure.apply(ctx, modelEntity) const structureEntity = await ModelToStructure.apply(ctx, modelEntity)
StructureToBond.apply(ctx, structureEntity, props)
return StructureToSpacefill.apply(ctx, structureEntity, props) return StructureToSpacefill.apply(ctx, structureEntity, props)
}) })
......
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