/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose * @author David Sehnal */ import { Structure, StructureSymmetry, Unit } from 'mol-model/structure'; import { Task } from 'mol-task' import { RenderObject } from 'mol-gl/render-object'; import { Representation, RepresentationProps, Visual } from '..'; import { ColorTheme, SizeTheme } from '../../theme'; import { PickingId } from '../../util/picking'; import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci'; import { MarkerAction } from '../../util/marker-data'; export interface UnitsVisual

extends Visual { } export interface StructureVisual

extends Visual { } export interface StructureRepresentation

extends Representation { } export const DefaultStructureProps = { colorTheme: { name: 'instance-index' } as ColorTheme, sizeTheme: { name: 'physical' } as SizeTheme, alpha: 1, visible: true, doubleSided: false, depthMask: true, useFog: true, } export type StructureProps = Partial export function StructureRepresentation

(unitsVisualCtor: () => UnitsVisual

, structureVisualCtor?: () => StructureVisual

): StructureRepresentation

{ let unitsVisuals = new Map }>() let structureVisual: StructureVisual

| undefined let _props: Required

let _structure: Structure let _groups: ReadonlyArray function create(structure: Structure, props: P = {} as P) { _props = Object.assign({}, DefaultStructureProps, _props, props) return Task.create('Creating StructureRepresentation', async ctx => { if (!_structure) { _groups = structure.symmetryGroups; for (let i = 0; i < _groups.length; i++) { const group = _groups[i]; const visual = unitsVisualCtor() await visual.create(ctx, group, _props) unitsVisuals.set(group.hashCode, { visual, group }) } if (structureVisualCtor) { structureVisual = structureVisualCtor() await structureVisual.create(ctx, structure, _props) } } else { if (_structure.hashCode === structure.hashCode) { await update(_props) } else { _groups = structure.symmetryGroups; const newGroups: Unit.SymmetryGroup[] = [] const oldUnitsVisuals = unitsVisuals unitsVisuals = new Map() for (let i = 0; i < _groups.length; i++) { const group = _groups[i]; const visualGroup = oldUnitsVisuals.get(group.hashCode) if (visualGroup) { const { visual, group } = visualGroup if (!await visual.update(ctx, _props)) { await visual.create(ctx, group, _props) } oldUnitsVisuals.delete(group.hashCode) } else { newGroups.push(group) const visual = unitsVisualCtor() await visual.create(ctx, group, _props) unitsVisuals.set(group.hashCode, { visual, group }) } } // for new groups, reuse leftover visuals const unusedVisuals: UnitsVisual

[] = [] oldUnitsVisuals.forEach(({ visual }) => unusedVisuals.push(visual)) newGroups.forEach(async group => { const visual = unusedVisuals.pop() || unitsVisualCtor() await visual.create(ctx, group, _props) unitsVisuals.set(group.hashCode, { visual, group }) }) unusedVisuals.forEach(visual => visual.destroy()) if (structureVisual) { if (!await structureVisual.update(ctx, _props)) { await structureVisual.create(ctx, _structure, _props) } } } } _structure = structure }); } function update(props: P) { return Task.create('Updating StructureRepresentation', async ctx => { _props = Object.assign({}, DefaultStructureProps, _props, props) unitsVisuals.forEach(async ({ visual, group }) => { if (!await visual.update(ctx, _props)) { await visual.create(ctx, group, _props) } }) if (structureVisual) { if (!await structureVisual.update(ctx, _props)) { await structureVisual.create(ctx, _structure, _props) } } }) } function getLoci(pickingId: PickingId) { let loci: Loci = EmptyLoci unitsVisuals.forEach(({ visual }) => { const _loci = visual.getLoci(pickingId) if (!isEmptyLoci(_loci)) loci = _loci }) return loci } function mark(loci: Loci, action: MarkerAction) { unitsVisuals.forEach(({ visual }) => visual.mark(loci, action)) } function destroy() { unitsVisuals.forEach(({ visual }) => visual.destroy()) unitsVisuals.clear() } return { get renderObjects() { const renderObjects: RenderObject[] = [] unitsVisuals.forEach(({ visual }) => renderObjects.push(...visual.renderObjects)) return renderObjects }, create, update, getLoci, mark, destroy } }