Selaa lähdekoodia

wip, picking and flags

Alexander Rose 6 vuotta sitten
vanhempi
commit
77ccbb9e5e

+ 0 - 1
src/mol-app/ui/visualization/viewport.tsx

@@ -128,7 +128,6 @@ export class Viewport extends View<ViewportController, ViewportState, { noWebGl?
 
         viewer.didDraw.subscribe(() => {
             // this.setState({ imageData: viewer.getImageData() })
-            viewer.pick()
             this.setState({
                 images: {
                     'object': viewer.getImageData('pickObject'),

+ 2 - 1
src/mol-geo/representation/structure/index.ts

@@ -67,7 +67,8 @@ export const DefaultStructureProps = {
     alpha: 1,
     visible: true,
     doubleSided: false,
-    depthMask: true
+    depthMask: true,
+    hoverSelection: { objectId: -1, instanceId: -1, elementId: -1 } as PickingId
 }
 export type StructureProps = Partial<typeof DefaultStructureProps>
 

+ 10 - 9
src/mol-geo/representation/structure/point.ts

@@ -11,21 +11,18 @@ import { Unit, Element } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { fillSerial } from 'mol-gl/renderable/util';
 
-import { UnitsRepresentation } from './index';
+import { UnitsRepresentation, DefaultStructureProps } from './index';
 import VertexMap from '../../shape/vertex-map';
-import { ColorTheme, SizeTheme } from '../../theme';
-import { createTransforms, createColors, createSizes } from './utils';
+import { SizeTheme } from '../../theme';
+import { createTransforms, createColors, createSizes, createFlags } from './utils';
 import { deepEqual, defaults } from 'mol-util';
 import { SortedArray } from 'mol-data/int';
 import { RenderableState, PointValues } from 'mol-gl/renderable';
 import { PickingId } from '../../util/picking';
 
 export const DefaultPointProps = {
-    colorTheme: { name: 'instance-index' } as ColorTheme,
-    sizeTheme: { name: 'vdw' } as SizeTheme,
-    alpha: 1,
-    visible: true,
-    depthMask: true
+    ...DefaultStructureProps,
+    sizeTheme: { name: 'vdw' } as SizeTheme
 }
 export type PointProps = Partial<typeof DefaultPointProps>
 
@@ -69,7 +66,7 @@ export default function Point(): UnitsRepresentation<PointProps> {
                 _units = group.units
                 _elements = group.elements;
 
-                const { colorTheme, sizeTheme } = currentProps
+                const { colorTheme, sizeTheme, hoverSelection } = currentProps
                 const elementCount = _elements.length
 
                 const vertexMap = VertexMap.create(
@@ -91,6 +88,9 @@ export default function Point(): UnitsRepresentation<PointProps> {
                 await ctx.update('Computing point sizes');
                 const size = createSizes(group, vertexMap, sizeTheme)
 
+                await ctx.update('Computing spacefill flags');
+                const flag = createFlags(group, hoverSelection.instanceId, hoverSelection.elementId)
+
                 const instanceCount = group.units.length
 
                 const values: PointValues = {
@@ -99,6 +99,7 @@ export default function Point(): UnitsRepresentation<PointProps> {
                     aTransform: transforms,
                     aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
                     ...color,
+                    ...flag,
                     ...size,
 
                     uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),

+ 11 - 2
src/mol-geo/representation/structure/spacefill.ts

@@ -14,7 +14,7 @@ import { Unit, Element, Queries } from 'mol-model/structure';
 import { UnitsRepresentation, DefaultStructureProps } from './index';
 import { Task } from 'mol-task'
 import { MeshBuilder } from '../../shape/mesh-builder';
-import { createTransforms, createColors } from './utils';
+import { createTransforms, createColors, createFlags } from './utils';
 import VertexMap from '../../shape/vertex-map';
 import { icosahedronVertexCount } from '../../primitive/icosahedron';
 import { deepEqual, defaults } from 'mol-util';
@@ -92,7 +92,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                 renderObjects.length = 0 // clear
                 currentGroup = group
 
-                const { detail, colorTheme } = { ...DefaultSpacefillProps, ...props }
+                const { detail, colorTheme, hoverSelection } = { ...DefaultSpacefillProps, ...props }
 
                 mesh = await createSpacefillMesh(group.units[0], detail).runAsChild(ctx, 'Computing spacefill mesh')
                 // console.log(mesh)
@@ -104,6 +104,9 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                 await ctx.update('Computing spacefill colors');
                 const color = createColors(group, vertexMap, colorTheme)
 
+                await ctx.update('Computing spacefill flags');
+                const flag = createFlags(group, hoverSelection.instanceId, hoverSelection.elementId)
+
                 const instanceCount = group.units.length
 
                 const values: MeshValues = {
@@ -111,6 +114,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                     aTransform: transforms,
                     aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
                     ...color,
+                    ...flag,
 
                     uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
                     uInstanceCount: ValueCell.create(instanceCount),
@@ -159,6 +163,11 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                     createColors(currentGroup, vertexMap, newProps.colorTheme, spheres.values)
                 }
 
+                if (newProps.hoverSelection !== currentProps.hoverSelection) {
+                    await ctx.update('Computing spacefill flags');
+                    createFlags(currentGroup, newProps.hoverSelection.instanceId, newProps.hoverSelection.elementId, spheres.values)
+                }
+
                 ValueCell.updateIfChanged(spheres.values.uAlpha, newProps.alpha)
                 ValueCell.updateIfChanged(spheres.values.dDoubleSided, newProps.doubleSided)
                 ValueCell.updateIfChanged(spheres.values.dFlipSided, newProps.flipSided)

+ 40 - 1
src/mol-geo/representation/structure/utils.ts

@@ -6,7 +6,7 @@
  */
 
 import { Unit } from 'mol-model/structure';
-import { Mat4 } from 'mol-math/linear-algebra'
+import { Mat4, Vec2 } from 'mol-math/linear-algebra'
 
 import { createUniformColor, ColorData } from '../../util/color-data';
 import { createUniformSize } from '../../util/size-data';
@@ -15,6 +15,7 @@ import VertexMap from '../../shape/vertex-map';
 import { ColorTheme, SizeTheme } from '../../theme';
 import { elementIndexColorData, elementSymbolColorData, instanceIndexColorData, chainIdColorData } from '../../theme/structure/color';
 import { ValueCell } from 'mol-util';
+import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 
 export function createTransforms({ units }: Unit.SymmetryGroup, transforms?: ValueCell<Float32Array>) {
     const unitCount = units.length
@@ -48,4 +49,42 @@ export function createSizes(group: Unit.SymmetryGroup, vertexMap: VertexMap, pro
         case 'vdw':
             return elementSizeData({ group, vertexMap })
     }
+}
+
+export type FlagData = {
+    tFlag: ValueCell<TextureImage>
+    uFlagTexSize: ValueCell<Vec2>
+}
+
+export function createFlags(group: Unit.SymmetryGroup, instanceId: number, elementId: number, flagData?: FlagData): FlagData {
+    const instanceCount = group.units.length
+    const elementCount = group.elements.length
+    const count = instanceCount * elementCount
+    const flags = flagData && flagData.tFlag.ref.value.array.length >= count ? flagData.tFlag.ref.value : createTextureImage(count, 1)
+    let flagOffset = 0
+    for (let i = 0; i < instanceCount; i++) {
+        for (let j = 0, jl = elementCount; j < jl; ++j) {
+            flags.array[flagOffset] = (i === instanceId && j === elementId) ? 255 : 0
+            flagOffset += 1
+        }
+    }
+    // console.log(flags, instanceCount, elementCount)
+    if (flagData) {
+        ValueCell.update(flagData.tFlag, flags)
+        ValueCell.update(flagData.uFlagTexSize, Vec2.create(flags.width, flags.height))
+        return flagData
+    } else {
+        return {
+            tFlag: ValueCell.create(flags),
+            uFlagTexSize: ValueCell.create(Vec2.create(flags.width, flags.height)),
+        }
+    }
+}
+
+const emptyFlagTexture = { array: new Uint8Array(1), width: 1, height: 1 }
+export function createEmptyFlags() {
+    return {
+        tFlag: ValueCell.create(emptyFlagTexture),
+        uFlagTexSize: ValueCell.create(Vec2.create(1, 1)),
+    }
 }

+ 3 - 0
src/mol-geo/representation/volume/surface.ts

@@ -18,6 +18,7 @@ import { createUniformColor } from '../../util/color-data';
 import { getMeshData } from '../../util/mesh-data';
 import { RenderableState, MeshValues } from 'mol-gl/renderable';
 import { PickingId } from '../../util/picking';
+import { createEmptyFlags } from '../structure/utils';
 
 export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue) {
     return Task.create<Mesh>('Volume Surface', async ctx => {
@@ -66,12 +67,14 @@ export default function Surface(): VolumeElementRepresentation<SurfaceProps> {
 
                 const instanceCount = 1
                 const color = createUniformColor({ value: 0x7ec0ee })
+                const flag = createEmptyFlags()
 
                 const values: MeshValues = {
                     ...getMeshData(mesh),
                     aTransform: ValueCell.create(new Float32Array(Mat4.identity())),
                     aInstanceId: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
                     ...color,
+                    ...flag,
 
                     uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
                     uInstanceCount: ValueCell.create(instanceCount),

+ 14 - 8
src/mol-geo/util/color-data.ts

@@ -5,7 +5,7 @@
  */
 
 import { ValueCell } from 'mol-util';
-import { TextureImage, createColorTexture, emptyTexture } from 'mol-gl/renderable/util';
+import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 import { Color } from 'mol-util/color';
 import VertexMap from '../shape/vertex-map';
 import { Vec2, Vec3 } from 'mol-math/linear-algebra';
@@ -20,6 +20,14 @@ export type ColorData = {
     dColorType: ValueCell<string>,
 }
 
+const emptyColorTexture = { array: new Uint8Array(3), width: 1, height: 1 }
+function createEmptyColorTexture() {
+    return {
+        tColor: ValueCell.create(emptyColorTexture),
+        uColorTexSize: ValueCell.create(Vec2.create(1, 1))
+    }
+}
+
 export interface UniformColorProps {
     value: Color
 }
@@ -36,8 +44,7 @@ export function createUniformColor(props: UniformColorProps, colorData?: ColorDa
         return {
             uColor: ValueCell.create(Color.toRgbNormalized(props.value) as Vec3),
             aColor: ValueCell.create(new Float32Array(0)),
-            tColor: ValueCell.create(emptyTexture),
-            uColorTexSize: ValueCell.create(Vec2.zero()),
+            ...createEmptyColorTexture(),
             dColorType: ValueCell.create('uniform'),
         }
     }
@@ -71,8 +78,7 @@ export function createAttributeColor(props: AttributeColorProps, colorData?: Col
         return {
             uColor: ValueCell.create(Vec3.zero()),
             aColor: ValueCell.create(colors),
-            tColor: ValueCell.create(emptyTexture),
-            uColorTexSize: ValueCell.create(Vec2.zero()),
+            ...createEmptyColorTexture(),
             dColorType: ValueCell.create('attribute'),
         }
     }
@@ -105,7 +111,7 @@ export interface InstanceColorProps {
 /** Creates color texture with color for each instance/unit */
 export function createInstanceColor(props: InstanceColorProps, colorData?: ColorData): ColorData {
     const { colorFn, instanceCount} = props
-    const colors = colorData && colorData.tColor.ref.value.array.length >= instanceCount * 3 ? colorData.tColor.ref.value : createColorTexture(instanceCount)
+    const colors = colorData && colorData.tColor.ref.value.array.length >= instanceCount * 3 ? colorData.tColor.ref.value : createTextureImage(instanceCount, 3)
     for (let i = 0; i < instanceCount; i++) {
         Color.toArray(colorFn(i), colors.array, i * 3)
     }
@@ -121,7 +127,7 @@ export interface ElementColorProps {
 export function createElementColor(props: ElementColorProps, colorData?: ColorData): ColorData {
     const { colorFn, vertexMap } = props
     const elementCount = vertexMap.offsetCount - 1
-    const colors = colorData && colorData.tColor.ref.value.array.length >= elementCount * 3 ? colorData.tColor.ref.value : createColorTexture(elementCount)
+    const colors = colorData && colorData.tColor.ref.value.array.length >= elementCount * 3 ? colorData.tColor.ref.value : createTextureImage(elementCount, 3)
     for (let i = 0, il = elementCount; i < il; ++i) {
         Color.toArray(colorFn(i), colors.array, i * 3)
     }
@@ -139,7 +145,7 @@ export function createElementInstanceColor(props: ElementInstanceColorProps, col
     const { colorFn, instanceCount, vertexMap } = props
     const elementCount = vertexMap.offsetCount - 1
     const count = instanceCount * elementCount
-    const colors = colorData && colorData.tColor.ref.value.array.length >= count * 3 ? colorData.tColor.ref.value : createColorTexture(count)
+    const colors = colorData && colorData.tColor.ref.value.array.length >= count * 3 ? colorData.tColor.ref.value : createTextureImage(count, 3)
     let colorOffset = 0
     for (let i = 0; i < instanceCount; i++) {
         for (let j = 0, jl = elementCount; j < jl; ++j) {

+ 4 - 1
src/mol-gl/_spec/renderer.spec.ts

@@ -19,6 +19,7 @@ import { RenderableState } from '../renderable';
 import { createPointRenderObject } from '../render-object';
 import { PointValues } from '../renderable/point';
 import Scene from '../scene';
+import { createEmptyFlags } from 'mol-geo/representation/structure/utils';
 
 // function writeImage(gl: WebGLRenderingContext, width: number, height: number) {
 //     const pixels = new Uint8Array(width * height * 4)
@@ -48,6 +49,7 @@ function createPoints() {
     const aInstanceId = ValueCell.create(fillSerial(new Float32Array(1)))
     const color = createUniformColor({ value: 0xFF0000 })
     const size = createUniformSize({ value: 1 })
+    const flag = createEmptyFlags()
 
     const aTransform = ValueCell.create(new Float32Array(16))
     const m4 = Mat4.identity()
@@ -59,6 +61,7 @@ function createPoints() {
         aTransform,
         aInstanceId,
         ...color,
+        ...flag,
         ...size,
 
         uAlpha: ValueCell.create(1.0),
@@ -108,7 +111,7 @@ describe('renderer', () => {
 
         scene.add(points)
         expect(ctx.bufferCount).toBe(6);
-        expect(ctx.textureCount).toBe(1);
+        expect(ctx.textureCount).toBe(2);
         expect(ctx.vaoCount).toBe(4);
         expect(ctx.programCache.count).toBe(4);
         expect(ctx.shaderCache.count).toBe(8);

+ 3 - 1
src/mol-gl/renderable/schema.ts

@@ -144,10 +144,12 @@ export const BaseSchema = {
     uAlpha: UniformSpec('f'),
     uInstanceCount: UniformSpec('i'),
     uElementCount: UniformSpec('i'),
-    uColorTexSize: UniformSpec('v2'),
     uColor: UniformSpec('v3'),
+    uColorTexSize: UniformSpec('v2'),
+    uFlagTexSize: UniformSpec('v2'),
 
     tColor: TextureSpec('rgb', 'ubyte'),
+    tFlag: TextureSpec('alpha', 'ubyte'),
 
     drawCount: ValueSpec('number'),
     instanceCount: ValueSpec('number'),

+ 2 - 4
src/mol-gl/renderable/util.ts

@@ -18,13 +18,11 @@ export interface TextureImage {
     height: number
 }
 
-export function createColorTexture (n: number): TextureImage {
-    const { length, width, height } = calculateTextureInfo(n, 3)
+export function createTextureImage (n: number, itemSize: number): TextureImage {
+    const { length, width, height } = calculateTextureInfo(n, itemSize)
     return { array: new Uint8Array(length), width, height }
 }
 
-export const emptyTexture = { array: new Uint8Array(0), width: 0, height: 0 }
-
 export function fillSerial<T extends Helpers.NumberArray> (array: T) {
     const n = array.length
     for (let i = 0; i < n; ++i) array[ i ] = i

+ 4 - 4
src/mol-gl/shader/chunks/color-assign-varying.glsl

@@ -1,11 +1,11 @@
 #if defined(dColorType_attribute)
-    vColor.xyz = aColor;
+    vColor.rgb = aColor;
 #elif defined(dColorType_instance)
-    vColor.xyz = read_vec3(tColor, aInstanceId, uColorTexSize);
+    vColor.rgb = readFromTexture(tColor, aInstanceId, uColorTexSize).rgb;
 #elif defined(dColorType_element)
-    vColor.xyz = read_vec3(tColor, aElementId, uColorTexSize);
+    vColor.rgb = readFromTexture(tColor, aElementId, uColorTexSize).rgb;
 #elif defined(dColorType_elementInstance)
-    vColor.xyz = read_vec3(tColor, aInstanceId * float(uElementCount) + aElementId, uColorTexSize);
+    vColor.rgb = readFromTexture(tColor, aInstanceId * float(uElementCount) + aElementId, uColorTexSize).rgb;
 #elif defined(dColorType_objectPicking)
     vColor = encodeIdRGBA(float(uObjectId));
 #elif defined(dColorType_instancePicking)

+ 1 - 3
src/mol-gl/shader/chunks/color-vert-params.glsl

@@ -10,6 +10,4 @@
 #elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_elementPicking)
     varying vec4 vColor;
     #pragma glslify: encodeIdRGBA = require(../utils/encode-id-rgba.glsl)
-#endif
-
-#pragma glslify: read_vec3 = require(../utils/read-from-texture.glsl)
+#endif

+ 3 - 1
src/mol-gl/shader/chunks/common-frag-params.glsl

@@ -1,3 +1,5 @@
 uniform int uObjectId;
 uniform int uInstanceCount;
-uniform int uElementCount;
+uniform int uElementCount;
+
+varying float vFlag;

+ 6 - 1
src/mol-gl/shader/chunks/common-vert-params.glsl

@@ -2,4 +2,9 @@ uniform mat4 uProjection, uModel, uView;
 
 uniform int uObjectId;
 uniform int uInstanceCount;
-uniform int uElementCount;
+uniform int uElementCount;
+
+uniform vec2 uFlagTexSize;
+uniform sampler2D tFlag;
+varying float vFlag;
+#pragma glslify: readFromTexture = require(../utils/read-from-texture.glsl)

+ 4 - 0
src/mol-gl/shader/mesh.frag

@@ -75,5 +75,9 @@ void main() {
         // gl_FragColor.rgb = vec3(1.0, 0.0, 0.0);
         gl_FragColor.rgb = finalColor;
         gl_FragColor.a = uAlpha;
+
+        if (vFlag == 1.0) {
+            gl_FragColor.rgb = mix(vec3(1.0, 0.4, 0.6), gl_FragColor.rgb, 0.3);
+        }
     #endif
 }

+ 1 - 0
src/mol-gl/shader/mesh.vert

@@ -27,6 +27,7 @@ varying vec3 vViewPosition;
 
 void main(){
     #pragma glslify: import('./chunks/color-assign-varying.glsl')
+    vFlag = readFromTexture(tFlag, aInstanceId * float(uElementCount) + aElementId, uFlagTexSize).a;
 
     mat4 modelView = uView * uModel * aTransform;
     vec4 mvPosition = modelView * vec4(aPosition, 1.0);

+ 3 - 3
src/mol-gl/shader/utils/read-from-texture.glsl

@@ -4,10 +4,10 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-vec3 read_vec3 (const in sampler2D tex, const in float i, const in vec2 size) {
+vec4 readFromTexture (const in sampler2D tex, const in float i, const in vec2 size) {
     float x = mod(i, size.x);
     float y = floor(i / size.x);
     vec2 uv = (vec2(x, y) + 0.5) / size;
-    return texture2D(tex, uv).rgb;
+    return texture2D(tex, uv);
 }
-#pragma glslify: export(read_vec3)
+#pragma glslify: export(readFromTexture)

+ 7 - 5
src/mol-gl/webgl/context.ts

@@ -112,11 +112,13 @@ export function createContext(gl: WebGLRenderingContext): Context {
 
         unbindFramebuffer: () => unbindFramebuffer(gl),
         readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => {
-            if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
-                gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer)
-            } else {
-                console.error('Reading pixels failed. Framebuffer not complete.')
-            }
+            gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer)
+            // TODO check is very expensive
+            // if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
+            //     gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer)
+            // } else {
+            //     console.error('Reading pixels failed. Framebuffer not complete.')
+            // }
         },
 
         destroy: () => {

+ 2 - 1
src/mol-gl/webgl/texture.ts

@@ -13,13 +13,14 @@ import { Framebuffer } from './framebuffer';
 
 const getNextTextureId = idFactory()
 
-export type TextureFormat = 'rgb' | 'rgba'
+export type TextureFormat = 'alpha' | 'rgb' | 'rgba'
 export type TextureType = 'ubyte' | 'uint'
 export type TextureAttachment = 'depth' | 'stencil' | 'color0'
 
 export function getFormat(ctx: Context, format: TextureFormat) {
     const { gl } = ctx
     switch (format) {
+        case 'alpha': return gl.ALPHA
         case 'rgb': return gl.RGB
         case 'rgba': return gl.RGBA
     }

+ 27 - 11
src/mol-view/viewer.ts

@@ -81,7 +81,14 @@ namespace Viewer {
             let label = ''
             reprMap.forEach((roSet, repr) => {
                 const info = repr.getLabel(p)
-                if (info) label = info.label
+                if (info) {
+                    label = info.label
+                    // TODO do better, take objectId fully into account
+                    repr.update({ hoverSelection: p }).run().then(() => {
+                        scene.update()
+                        requestDraw()
+                    })
+                }
             })
             identified.next(`Object: ${p.objectId}, Instance: ${p.instanceId}, Element: ${p.elementId}, Label: ${label}`)
         })
@@ -117,6 +124,7 @@ namespace Viewer {
         const instancePickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
         const elementPickTarget = createRenderTarget(ctx, pickWidth, pickHeight)
 
+        let pickDirty = true
         let drawPending = false
         const prevProjectionView = Mat4.zero()
 
@@ -127,6 +135,10 @@ namespace Viewer {
             if (force || !Mat4.areEqual(camera.projectionView, prevProjectionView, EPSILON.Value)) {
                 Mat4.copy(prevProjectionView, camera.projectionView)
                 renderer.render(scene, variant)
+                if (variant === 'draw') {
+                    pickDirty = true
+                    pick()
+                }
                 didRender = true
             }
             return didRender
@@ -153,6 +165,19 @@ namespace Viewer {
             window.requestAnimationFrame(() => animate())
         }
 
+        function pick() {
+            objectPickTarget.bind()
+            render('pickObject', pickDirty)
+
+            instancePickTarget.bind()
+            render('pickInstance', pickDirty)
+
+            elementPickTarget.bind()
+            render('pickElement', pickDirty)
+
+            pickDirty = false
+        }
+
         function identify (x: number, y: number): PickingId {
             const buffer = new Uint8Array(4)
             y = canvas.height - y // flip y
@@ -222,16 +247,7 @@ namespace Viewer {
             draw,
             requestDraw,
             animate,
-            pick: () => {
-                objectPickTarget.bind()
-                render('pickObject', true)
-
-                instancePickTarget.bind()
-                render('pickInstance', true)
-
-                elementPickTarget.bind()
-                render('pickElement', true)
-            },
+            pick,
             identify,
 
             handleResize,