Skip to content
Snippets Groups Projects
Select Git revision
  • 05ce3d9a212bc9c69e88bc1d157cea4b1f9fc636
  • master default protected
  • rednatco-v2
  • rednatco
  • test
  • ntc-tube-uniform-color
  • ntc-tube-missing-atoms
  • restore-vertex-array-per-program
  • watlas2
  • dnatco_new
  • cleanup-old-nodejs
  • webmmb
  • fix_auth_seq_id
  • update_deps
  • ext_dev
  • ntc_balls
  • nci-2
  • plugin
  • bugfix-0.4.5
  • nci
  • servers
  • v0.5.0-dev.1
  • v0.4.5
  • v0.4.4
  • v0.4.3
  • v0.4.2
  • v0.4.1
  • v0.4.0
  • v0.3.12
  • v0.3.11
  • v0.3.10
  • v0.3.9
  • v0.3.8
  • v0.3.7
  • v0.3.6
  • v0.3.5
  • v0.3.4
  • v0.3.3
  • v0.3.2
  • v0.3.1
  • v0.3.0
41 results

representation.ts

Blame
  • user avatar
    Alexander Rose authored
    05ce3d9a
    History
    representation.ts 8.86 KiB
    /**
     * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
     *
     * @author Alexander Rose <alexander.rose@weirdbyte.de>
     */
    
    import { Task } from 'mol-task'
    import { Representation, Visual, RepresentationContext, VisualContext, RepresentationProvider, RepresentationParamsGetter } from '../representation';
    import { VolumeData } from 'mol-model/volume';
    import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
    import { Geometry, updateRenderableState } from 'mol-geo/geometry/geometry';
    import { ParamDefinition as PD } from 'mol-util/param-definition';
    import { PickingId } from 'mol-geo/geometry/picking';
    import { MarkerAction, applyMarkerAction } from 'mol-geo/geometry/marker-data';
    import { DirectVolumeRenderObject, PointsRenderObject, LinesRenderObject, MeshRenderObject } from 'mol-gl/render-object';
    import { Interval } from 'mol-data/int';
    import { RenderableValues } from 'mol-gl/renderable/schema';
    import { LocationIterator } from 'mol-geo/util/location-iterator';
    import { NullLocation } from 'mol-model/location';
    import { VisualUpdateState } from 'mol-repr/util';
    import { ValueCell } from 'mol-util';
    import { Theme, createTheme } from 'mol-theme/theme';
    import { Subject } from 'rxjs';
    
    export interface VolumeVisual<P extends VolumeParams> extends Visual<VolumeData, P> { }
    
    type VolumeRenderObject = MeshRenderObject | LinesRenderObject | PointsRenderObject | DirectVolumeRenderObject
    
    interface VolumeVisualBuilder<P extends VolumeParams, G extends Geometry> {
        defaultProps: PD.Values<P>
        createGeometry(ctx: VisualContext, volumeData: VolumeData, props: PD.Values<P>, geometry?: G): Promise<G>
        getLoci(pickingId: PickingId, id: number): Loci
        mark(loci: Loci, apply: (interval: Interval) => boolean): boolean
        setUpdateState(state: VisualUpdateState, newProps: PD.Values<P>, currentProps: PD.Values<P>): void
    }
    
    interface VolumeVisualGeometryBuilder<P extends VolumeParams, G extends Geometry> extends VolumeVisualBuilder<P, G> {
        createRenderObject(ctx: VisualContext, geometry: G, locationIt: LocationIterator, theme: Theme, currentProps: PD.Values<P>): Promise<VolumeRenderObject>
        updateValues(values: RenderableValues, geometry: G, newProps: PD.Values<P>): void
    }
    
    export function VolumeVisual<P extends VolumeParams>(builder: VolumeVisualGeometryBuilder<P, Geometry>): VolumeVisual<P> {
        const { defaultProps, createGeometry, getLoci, mark, setUpdateState } = builder
        const { createRenderObject, updateValues } = builder
        const updateState = VisualUpdateState.create()
    
        let currentProps: PD.Values<P>
        let renderObject: VolumeRenderObject | undefined
        let currentVolume: VolumeData
        let geometry: Geometry
        let locationIt: LocationIterator
    
        async function create(ctx: VisualContext, volume: VolumeData, theme: Theme, props: Partial<PD.Values<P>> = {}) {
            currentProps = Object.assign({}, defaultProps, props)
            geometry = await createGeometry(ctx, volume, currentProps, geometry)
            locationIt = LocationIterator(1, 1, () => NullLocation)
            renderObject = await createRenderObject(ctx, geometry, locationIt, theme, currentProps)
        }
    
        async function update(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}) {
            if (!renderObject) return
            const newProps = Object.assign({}, currentProps, props)
    
            VisualUpdateState.reset(updateState)
            setUpdateState(updateState, newProps, currentProps)
    
            if (updateState.createGeometry) {
                geometry = await createGeometry(ctx, currentVolume, currentProps, geometry)
                ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(geometry))
            }
    
            updateValues(renderObject.values, geometry, newProps)
            updateRenderableState(renderObject.state, newProps)
    
            currentProps = newProps
        }
    
        return {
            get groupCount() { return locationIt ? locationIt.count : 0 },
            get renderObject () { return renderObject },
            async createOrUpdate(ctx: VisualContext, theme: Theme, props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
                if (!volume && !currentVolume) {
                    throw new Error('missing volume')
                } else if (volume && (!currentVolume || !renderObject)) {
                    currentVolume = volume
                    await create(ctx, volume, theme, props)
                } else if (volume && volume !== currentVolume) {
                    currentVolume = volume
                    await create(ctx, volume, theme, props)
                } else {
                    await update(ctx, theme, props)
                }
    
                currentProps = Object.assign({}, defaultProps, props)
            },
            getLoci(pickingId: PickingId) {
                return renderObject ? getLoci(pickingId, renderObject.id) : EmptyLoci
            },
            mark(loci: Loci, action: MarkerAction) {
                if (!renderObject) return false
                const { tMarker } = renderObject.values
                const { groupCount, instanceCount } = locationIt
    
                function apply(interval: Interval) {
                    const start = Interval.start(interval)
                    const end = Interval.end(interval)
                    return applyMarkerAction(tMarker.ref.value.array, start, end, action)
                }
    
                let changed = false
                if (isEveryLoci(loci)) {
                    changed = apply(Interval.ofBounds(0, groupCount * instanceCount))
                } else {
                    changed = mark(loci, apply)
                }
                if (changed) {
                    ValueCell.update(tMarker, tMarker.ref.value)
                }
                return changed
            },
            setVisibility(value: boolean) {
                if (renderObject) renderObject.state.visible = value
            },
            setPickable(value: boolean) {
                if (renderObject) renderObject.state.pickable = value
            },
            destroy() {
                // TODO
                renderObject = undefined
            }
        }
    }
    
    export interface VolumeRepresentation<P extends VolumeParams> extends Representation<VolumeData, P> { }
    
    export type VolumeRepresentationProvider<P extends VolumeParams> = RepresentationProvider<VolumeData, P>
    
    //
    
    export const VolumeParams = {
        ...Geometry.Params,
        isoValue: PD.Numeric(0.22, { min: -1, max: 1, step: 0.01 }),
    }
    export type VolumeParams = typeof VolumeParams
    
    export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<VolumeData, P>, visualCtor: (volume: VolumeData) => VolumeVisual<P>): VolumeRepresentation<P> {
        let version = 0
        const updated = new Subject<number>()
        const _state = Representation.createState()
        let visual: VolumeVisual<P>
    
        let _volume: VolumeData
        let _props: PD.Values<P>
        let _params: P
        let _theme: Theme
        let busy = false
    
        function createOrUpdate(props: Partial<PD.Values<P>> = {}, volume?: VolumeData) {
            if (volume && volume !== _volume) {
                _params = getParams(ctx, volume)
                _volume = volume
                if (!_props) _props = PD.getDefaultValues(_params)
            }
            _props = Object.assign({}, _props, props)
            _theme = createTheme(ctx, _props, {}, _theme)
    
            return Task.create('VolumeRepresentation.create', async runtime => {
                // TODO queue it somehow
                if (busy) return
    
                if (!visual && !volume) {
                    throw new Error('volume data missing')
                } else if (volume && !visual) {
                    busy = true
                    visual = visualCtor(volume)
                    await visual.createOrUpdate({ webgl: ctx.webgl, runtime }, _theme, _props, volume)
                    busy = false
                } else {
                    busy = true
                    await visual.createOrUpdate({ webgl: ctx.webgl, runtime }, _theme, _props, volume)
                    busy = false
                }
                updated.next(version++)
            });
        }
    
        function getLoci(pickingId: PickingId) {
            return visual ? visual.getLoci(pickingId) : EmptyLoci
        }
    
        function mark(loci: Loci, action: MarkerAction) {
            return visual ? visual.mark(loci, action) : false
        }
    
        function setState(state: Partial<Representation.State>) {
            if (state.visible !== undefined && visual) visual.setVisibility(state.visible)
            if (state.pickable !== undefined && visual) visual.setPickable(state.pickable)
    
            Representation.updateState(_state, state)
        }
    
        function destroy() {
            if (visual) visual.destroy()
        }
    
        return {
            label,
            get groupCount() {
                return visual ? visual.groupCount : 0
            },
            get renderObjects() {
                return visual && visual.renderObject ? [ visual.renderObject ] : []
            },
            get props () { return _props },
            get params() { return _params },
            get state() { return _state },
            updated,
            createOrUpdate,
            setState,
            getLoci,
            mark,
            destroy
        }
    }