Newer
Older
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Structure } from 'mol-model/structure';
import { Visual, VisualContext } from '../visual';
import { createRenderObject, GraphicsRenderObject } from 'mol-gl/render-object';
import { UnitKind, UnitKindOptions } from './visual/util/common';
import { StructureMeshParams, StructureParams, StructureDirectVolumeParams } from './representation';
import { deepEqual, ValueCell } from 'mol-util';
import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
import { Interval } from 'mol-data/int';
import { ParamDefinition as PD } from 'mol-util/param-definition';
import { createSizes } from 'mol-geo/geometry/size-data';
import { Geometry, GeometryUtils } from 'mol-geo/geometry/geometry';
import { LocationIterator } from 'mol-geo/util/location-iterator';
import { PickingId } from 'mol-geo/geometry/picking';
import { createColors } from 'mol-geo/geometry/color-data';
import { MarkerAction } from 'mol-geo/geometry/marker-data';
import { Mesh } from 'mol-geo/geometry/mesh/mesh';
import { VisualUpdateState } from 'mol-repr/util';
import { Theme, createEmptyTheme } from 'mol-theme/theme';
import { ColorTheme } from 'mol-theme/color';
import { SizeTheme } from 'mol-theme/size';
import { UnitsParams } from './units-representation';
import { DirectVolume } from 'mol-geo/geometry/direct-volume/direct-volume';
import { createIdentityTransform } from 'mol-geo/geometry/transform-data';
import { Transparency } from 'mol-theme/transparency';
export interface ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
function createComplexRenderObject<G extends Geometry>(structure: Structure, geometry: G, locationIt: LocationIterator, theme: Theme, props: PD.Values<Geometry.Params<G>>, materialId: number) {
const { createValues, createRenderableState } = Geometry.getUtils(geometry)
const values = createValues(geometry, transform, locationIt, theme, props)
return createRenderObject(geometry.kind, values, state, materialId)
const ComplexParams = {
...StructureParams,
unitKinds: PD.MultiSelect<UnitKind>(['atomic', 'spheres'], UnitKindOptions),
type ComplexParams = typeof ComplexParams
interface ComplexVisualBuilder<P extends ComplexParams, G extends Geometry> {
createGeometry(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<P>, geometry?: G): Promise<G> | G
createLocationIterator(structure: Structure): LocationIterator
getLoci(pickingId: PickingId, structure: Structure, id: number): Loci
eachLocation(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean): boolean,
setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme): void
interface ComplexVisualGeometryBuilder<P extends UnitsParams, G extends Geometry> extends ComplexVisualBuilder<P, G> {
geometryUtils: GeometryUtils<G>
export function ComplexVisual<G extends Geometry, P extends ComplexParams & Geometry.Params<G>>(builder: ComplexVisualGeometryBuilder<P, G>, materialId: number): ComplexVisual<P> {
const { defaultProps, createGeometry, createLocationIterator, getLoci, eachLocation, setUpdateState } = builder
const { updateValues, updateBoundingSphere, updateRenderableState } = builder.geometryUtils
const updateState = VisualUpdateState.create()
let newProps: PD.Values<P>
let newTheme: Theme
let currentProps: PD.Values<P> = Object.assign({}, defaultProps)
let currentTheme: Theme = createEmptyTheme()
let currentStructure: Structure
let locationIt: LocationIterator
function prepareUpdate(theme: Theme, props: Partial<PD.Values<P>>, structure: Structure) {
if (!structure && !currentStructure) {
throw new Error('missing structure')
}
newProps = Object.assign({}, currentProps, props)
if (!renderObject) {
updateState.createNew = true
} else if (!currentStructure || !Structure.areEquivalent(newStructure, currentStructure)) {
updateState.createNew = true
}
if (updateState.createNew) {
updateState.createGeometry = true
return
}
setUpdateState(updateState, newProps, currentProps, newTheme, currentTheme)
if (Structure.conformationHash(newStructure) !== Structure.conformationHash(currentStructure)) {
if (!ColorTheme.areEqual(theme.color, currentTheme.color)) updateState.updateColor = true
if (!deepEqual(newProps.unitKinds, currentProps.unitKinds)) updateState.createGeometry = true
updateState.updateColor = true
}
if (updateState.createNew) {
locationIt = createLocationIterator(newStructure)
renderObject = createComplexRenderObject(newStructure, newGeometry, locationIt, newTheme, newProps, materialId)
} else {
throw new Error('expected geometry to be given')
}
} else {
if (!renderObject) {
throw new Error('expected renderObject to be available')
}
locationIt.reset()
if (updateState.createGeometry) {
if (newGeometry) {
ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(newGeometry))
updateBoundingSphere(renderObject.values, newGeometry)
} else {
throw new Error('expected geometry to be given')
}
if (updateState.updateSize) {
// not all geometries have size data, so check here
if ('uSize' in renderObject.values) {
createSizes(locationIt, newTheme.size, renderObject.values)
}
}
if (updateState.updateColor) {
createColors(locationIt, newTheme.color, renderObject.values)
}
updateValues(renderObject.values, newProps)
updateRenderableState(renderObject.state, newProps)
}
currentProps = newProps
currentStructure = newStructure
if (newGeometry) geometry = newGeometry
function lociApply(loci: Loci, apply: (interval: Interval) => boolean) {
if (isEveryLoci(loci) || (Structure.isLoci(loci) && Structure.areEquivalent(loci.structure, currentStructure))) {
return apply(Interval.ofBounds(0, locationIt.groupCount * locationIt.instanceCount))
} else {
return eachLocation(loci, currentStructure, apply)
}
}
get groupCount() { return locationIt ? locationIt.count : 0 },
get renderObject () { return locationIt && locationIt.count ? renderObject : undefined },
createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, structure?: Structure) {
prepareUpdate(theme, props, structure || currentStructure)
if (updateState.createGeometry) {
const newGeometry = createGeometry(ctx, newStructure, newTheme, newProps, geometry)
return newGeometry instanceof Promise ? newGeometry.then(update) : update(newGeometry)
},
getLoci(pickingId: PickingId) {
return renderObject ? getLoci(pickingId, currentStructure, renderObject.id) : EmptyLoci
},
mark(loci: Loci, action: MarkerAction) {
return Visual.mark(renderObject, loci, action, lociApply)
setVisibility(visible: boolean) {
Visual.setVisibility(renderObject, visible)
setAlphaFactor(alphaFactor: number) {
Visual.setAlphaFactor(renderObject, alphaFactor)
},
setPickable(pickable: boolean) {
Visual.setPickable(renderObject, pickable)
Alexander Rose
committed
setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) {
Visual.setTransform(renderObject, matrix, instanceMatrices)
return Visual.setOverpaint(renderObject, overpaint, lociApply, true)
setTransparency(transparency: Transparency) {
return Visual.setTransparency(renderObject, transparency, lociApply, true)
},
destroy() {
// TODO
}
// mesh
export const ComplexMeshParams = {
...StructureMeshParams,
unitKinds: PD.MultiSelect<UnitKind>([ 'atomic', 'spheres' ], UnitKindOptions),
export type ComplexMeshParams = typeof ComplexMeshParams
export interface ComplexMeshVisualBuilder<P extends ComplexMeshParams> extends ComplexVisualBuilder<P, Mesh> { }
export function ComplexMeshVisual<P extends ComplexMeshParams>(builder: ComplexMeshVisualBuilder<P>, materialId: number): ComplexVisual<P> {
return ComplexVisual<Mesh, StructureMeshParams & UnitsParams>({
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
}
// direct-volume
export const ComplexDirectVolumeParams = {
...StructureDirectVolumeParams,
unitKinds: PD.MultiSelect<UnitKind>(['atomic', 'spheres', 'gaussians'], UnitKindOptions),
}
export type ComplexDirectVolumeParams = typeof ComplexDirectVolumeParams
export interface ComplexDirectVolumeVisualBuilder<P extends ComplexDirectVolumeParams> extends ComplexVisualBuilder<P, DirectVolume> { }
export function ComplexDirectVolumeVisual<P extends ComplexDirectVolumeParams>(builder: ComplexDirectVolumeVisualBuilder<P>, materialId: number): ComplexVisual<P> {
return ComplexVisual<DirectVolume, StructureDirectVolumeParams & UnitsParams>({
...builder,
setUpdateState: (state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>, newTheme: Theme, currentTheme: Theme) => {
builder.setUpdateState(state, newProps, currentProps, newTheme, currentTheme)
if (!SizeTheme.areEqual(newTheme.size, currentTheme.size)) state.createGeometry = true
},