From 0898c0352072d9d5f42aace16f4651c0f8c860eb Mon Sep 17 00:00:00 2001
From: Alexander Rose <alexander.rose@weirdbyte.de>
Date: Sat, 1 Sep 2018 09:23:02 -0700
Subject: [PATCH] point visual creation improvements

---
 .../structure/representation/point.ts         |   6 +-
 .../structure/visual/element-point.ts         | 100 +++++++-----------
 .../structure/visual/util/common.ts           |  58 ++++++++--
 src/mol-geo/representation/util.ts            |  17 ++-
 src/mol-geo/util/size-data.ts                 |  24 ++++-
 5 files changed, 126 insertions(+), 79 deletions(-)

diff --git a/src/mol-geo/representation/structure/representation/point.ts b/src/mol-geo/representation/structure/representation/point.ts
index 8d15e6555..6ea64af0e 100644
--- a/src/mol-geo/representation/structure/representation/point.ts
+++ b/src/mol-geo/representation/structure/representation/point.ts
@@ -8,10 +8,10 @@ import { UnitsRepresentation } from '..';
 import { ElementPointVisual, DefaultElementPointProps } from '../visual/element-point';
 import { StructureRepresentation } from '../units-representation';
 
-export const DefaultSpacefillProps = {
-    ...DefaultElementPointProps
+export const DefaultPointProps = {
+    ...DefaultElementPointProps,
 }
-export type PointProps = typeof DefaultElementPointProps
+export type PointProps = typeof DefaultPointProps
 
 export type PointRepresentation = StructureRepresentation<PointProps>
 
diff --git a/src/mol-geo/representation/structure/visual/element-point.ts b/src/mol-geo/representation/structure/visual/element-point.ts
index 0765138ae..88544cf76 100644
--- a/src/mol-geo/representation/structure/visual/element-point.ts
+++ b/src/mol-geo/representation/structure/visual/element-point.ts
@@ -6,19 +6,18 @@
  */
 
 import { ValueCell } from 'mol-util/value-cell'
-import { createPointRenderObject, PointRenderObject } from 'mol-gl/render-object'
+import { PointRenderObject } from 'mol-gl/render-object'
 import { Unit } from 'mol-model/structure';
 import { RuntimeContext } from 'mol-task'
 
 import { UnitsVisual, DefaultStructureProps } from '..';
-import { getElementLoci, StructureElementIterator } from './util/element';
-import { createTransforms, createColors, createSizes } from './util/common';
-import { deepEqual, defaults } from 'mol-util';
-import { SortedArray } from 'mol-data/int';
-import { RenderableState, PointValues } from 'mol-gl/renderable';
+import { getElementLoci, StructureElementIterator, markElement } from './util/element';
+import { createTransforms, createColors, createSizes, createUnitsPointRenderObject } from './util/common';
+import { deepEqual } from 'mol-util';
+import { Interval } from 'mol-data/int';
 import { PickingId } from '../../../util/picking';
-import { Loci, EmptyLoci } from 'mol-model/loci';
-import { MarkerAction, createMarkers } from '../../../util/marker-data';
+import { Loci, EmptyLoci, isEveryLoci } from 'mol-model/loci';
+import { MarkerAction, createMarkers, applyMarkerAction } from '../../../util/marker-data';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { fillSerial } from 'mol-util/array';
 import { SizeThemeProps } from 'mol-view/theme/size';
@@ -26,12 +25,12 @@ import { LocationIterator } from '../../../util/location-iterator';
 
 export const DefaultElementPointProps = {
     ...DefaultStructureProps,
-    sizeTheme: { name: 'physical' } as SizeThemeProps
+    sizeTheme: { name: 'physical' } as SizeThemeProps,
+    pointSizeAttenuation: true,
 }
 export type ElementPointProps = Partial<typeof DefaultElementPointProps>
 
-// TODO make async
-export function createElementPointVertices(unit: Unit, vertices?: ValueCell<Float32Array>) {
+export async function createElementPointVertices(ctx: RuntimeContext, unit: Unit, vertices?: ValueCell<Float32Array>) {
     const elements = unit.elements
     const n = elements.length * 3
     const array = vertices && vertices.ref.value.length >= n ? vertices.ref.value : new Float32Array(n)
@@ -44,6 +43,11 @@ export function createElementPointVertices(unit: Unit, vertices?: ValueCell<Floa
         array[i] = p[0]
         array[i + 1] = p[1]
         array[i + 2] = p[2]
+
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Creating points', current: i / 3, max: elements.length });
+        }
+        ++i
     }
     return vertices ? ValueCell.update(vertices, array) : ValueCell.create(array)
 }
@@ -53,9 +57,7 @@ export function ElementPointVisual(): UnitsVisual<ElementPointProps> {
     let currentProps = DefaultElementPointProps
     let currentGroup: Unit.SymmetryGroup
     let locationIt: LocationIterator
-
-    let _units: ReadonlyArray<Unit>
-    let _elements: SortedArray
+    let vertices: ValueCell<Float32Array>
 
     return {
         get renderObject () { return renderObject },
@@ -66,47 +68,9 @@ export function ElementPointVisual(): UnitsVisual<ElementPointProps> {
                 currentProps = Object.assign({}, DefaultElementPointProps, props)
                 currentGroup = group
                 locationIt = StructureElementIterator.fromGroup(group)
+                vertices = await createElementPointVertices(ctx, group.units[0], vertices)
 
-                _units = group.units
-                _elements = group.elements;
-
-                const { colorTheme, sizeTheme } = currentProps
-                const elementCount = _elements.length
-                const instanceCount = group.units.length
-
-                const vertices = createElementPointVertices(_units[0])
-                const transform = createTransforms(group)
-                // console.time('createColors point')
-                const color = await createColors(ctx, locationIt, colorTheme)
-                // console.timeEnd('createColors point')
-                const size = createSizes(locationIt, sizeTheme)
-                const marker = createMarkers(instanceCount * elementCount)
-
-                const values: PointValues = {
-                    aPosition: vertices,
-                    aGroup: ValueCell.create(fillSerial(new Float32Array(elementCount))),
-                    aInstance: ValueCell.create(fillSerial(new Float32Array(instanceCount))),
-                    ...transform,
-                    ...color,
-                    ...marker,
-                    ...size,
-
-                    uAlpha: ValueCell.create(defaults(props.alpha, 1.0)),
-                    uInstanceCount: ValueCell.create(instanceCount),
-                    uGroupCount: ValueCell.create(group.elements.length),
-
-                    drawCount: ValueCell.create(group.elements.length),
-                    instanceCount: ValueCell.create(instanceCount),
-
-                    dPointSizeAttenuation: ValueCell.create(true),
-                    dUseFog: ValueCell.create(defaults(props.useFog, true)),
-                }
-                const state: RenderableState = {
-                    depthMask: defaults(props.depthMask, true),
-                    visible: defaults(props.visible, true)
-                }
-
-                renderObject = createPointRenderObject(values, state)
+                renderObject = await createUnitsPointRenderObject(ctx, group, vertices, locationIt, currentProps)
             } else if (renderObject) {
                 if (group) currentGroup = group
 
@@ -134,9 +98,9 @@ export function ElementPointVisual(): UnitsVisual<ElementPointProps> {
                 }
 
                 if (createVertices) {
-                    createElementPointVertices(currentGroup.units[0], renderObject.values.aPosition)
+                    await createElementPointVertices(ctx, currentGroup.units[0], vertices)
                     ValueCell.update(renderObject.values.aGroup, fillSerial(new Float32Array(locationIt.groupCount))) // TODO reuse array
-                    ValueCell.update(renderObject.values.drawCount, currentGroup.elements.length)
+                    ValueCell.update(renderObject.values.drawCount, locationIt.groupCount)
                     updateColor = true
                     updateSize = true
                 }
@@ -146,7 +110,7 @@ export function ElementPointVisual(): UnitsVisual<ElementPointProps> {
                 }
 
                 if (updateSize) {
-                    createSizes(locationIt, newProps.sizeTheme, renderObject.values)
+                    await createSizes(ctx, locationIt, newProps.sizeTheme, renderObject.values)
                 }
 
                 currentProps = newProps
@@ -156,8 +120,26 @@ export function ElementPointVisual(): UnitsVisual<ElementPointProps> {
             return renderObject ? getElementLoci(pickingId, currentGroup, renderObject.id) : EmptyLoci
         },
         mark(loci: Loci, action: MarkerAction) {
-            // TODO
-            // markElement(loci, action, currentGroup, renderObject.values)
+            if (!renderObject) return
+            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)) {
+                apply(Interval.ofBounds(0, groupCount * instanceCount))
+                changed = true
+            } else {
+                changed = markElement(loci, currentGroup, apply)
+            }
+            if (changed) {
+                ValueCell.update(tMarker, tMarker.ref.value)
+            }
         },
         destroy() {
             // TODO
diff --git a/src/mol-geo/representation/structure/visual/util/common.ts b/src/mol-geo/representation/structure/visual/util/common.ts
index b55c3f265..e9770daa2 100644
--- a/src/mol-geo/representation/structure/visual/util/common.ts
+++ b/src/mol-geo/representation/structure/visual/util/common.ts
@@ -13,15 +13,17 @@ import { createUniformSize, SizeData, createGroupSize, createGroupInstanceSize,
 import { ValueCell } from 'mol-util';
 import { LocationIterator } from '../../../../util/location-iterator';
 import { Mesh } from '../../../../mesh/mesh';
-import { MeshValues } from 'mol-gl/renderable';
+import { MeshValues, PointValues } from 'mol-gl/renderable';
 import { getMeshData } from '../../../../util/mesh-data';
-import { MeshProps, createMeshValues, createRenderableState, createIdentityTransform, TransformData } from '../../../util';
+import { MeshProps, createMeshValues, createRenderableState, createIdentityTransform, TransformData, createPointValues } from '../../../util';
 import { StructureProps } from '../..';
 import { createMarkers } from '../../../../util/marker-data';
-import { createMeshRenderObject } from 'mol-gl/render-object';
+import { createMeshRenderObject, createPointRenderObject } from 'mol-gl/render-object';
 import { ColorThemeProps, ColorTheme } from 'mol-view/theme/color';
 import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size';
 import { RuntimeContext } from 'mol-task';
+import { PointProps } from 'mol-geo/representation/structure/representation/point';
+import { fillSerial } from 'mol-util/array';
 
 export function createTransforms({ units }: Unit.SymmetryGroup, transformData?: TransformData) {
     const unitCount = units.length
@@ -38,7 +40,7 @@ export function createTransforms({ units }: Unit.SymmetryGroup, transformData?:
     }
 }
 
-export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, props: ColorThemeProps, colorData?: ColorData) {
+export function createColors(ctx: RuntimeContext, locationIt: LocationIterator, props: ColorThemeProps, colorData?: ColorData): Promise<ColorData> {
     const colorTheme = ColorTheme(props)
     switch (colorTheme.kind) {
         case 'uniform': return createUniformColor(ctx, locationIt, colorTheme.color, colorData)
@@ -48,23 +50,23 @@ export function createColors(ctx: RuntimeContext, locationIt: LocationIterator,
     }
 }
 
-export function createSizes(locationIt: LocationIterator, props: SizeThemeProps, sizeData?: SizeData): SizeData {
+export async function createSizes(ctx: RuntimeContext, locationIt: LocationIterator, props: SizeThemeProps, sizeData?: SizeData): Promise<SizeData> {
     const sizeTheme = SizeTheme(props)
     switch (sizeTheme.kind) {
-        case 'uniform': return createUniformSize(locationIt, sizeTheme.size, sizeData)
-        case 'group': return createGroupSize(locationIt, sizeTheme.size, sizeData)
-        case 'groupInstance': return createGroupInstanceSize(locationIt, sizeTheme.size, sizeData)
-        case 'instance': return createInstanceSize(locationIt, sizeTheme.size, sizeData)
+        case 'uniform': return createUniformSize(ctx, locationIt, sizeTheme.size, sizeData)
+        case 'group': return createGroupSize(ctx, locationIt, sizeTheme.size, sizeData)
+        case 'groupInstance': return createGroupInstanceSize(ctx, locationIt, sizeTheme.size, sizeData)
+        case 'instance': return createInstanceSize(ctx, locationIt, sizeTheme.size, sizeData)
     }
 }
 
+// mesh
+
 type StructureMeshProps = Required<MeshProps & StructureProps>
 
 async function _createMeshValues(ctx: RuntimeContext, transforms: TransformData, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps): Promise<MeshValues> {
     const { instanceCount, groupCount } = locationIt
-    console.time('createColors mesh')
     const color = await createColors(ctx, locationIt, props.colorTheme)
-    console.timeEnd('createColors mesh')
     const marker = createMarkers(instanceCount * groupCount)
 
     const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
@@ -104,4 +106,38 @@ export async function createUnitsMeshRenderObject(ctx: RuntimeContext, group: Un
 export async function updateComplexMeshRenderObject(ctx: RuntimeContext, structure: Structure, mesh: Mesh, locationIt: LocationIterator, props: StructureMeshProps): Promise<MeshValues> {
     const transforms = createIdentityTransform()
     return _createMeshValues(ctx, transforms, mesh, locationIt, props)
+}
+
+// point
+
+type StructurePointProps = Required<PointProps & StructureProps>
+
+async function _createPointValues(ctx: RuntimeContext, transforms: TransformData, vertices: ValueCell<Float32Array>, locationIt: LocationIterator, props: StructurePointProps): Promise<PointValues> {
+    const { instanceCount, groupCount } = locationIt
+    const color = await createColors(ctx, locationIt, props.colorTheme)
+    const size = await createSizes(ctx, locationIt, props.sizeTheme)
+    const marker = createMarkers(instanceCount * groupCount)
+
+    const counts = { drawCount: groupCount, groupCount, instanceCount }
+
+    return {
+        aPosition: vertices,
+        aGroup: ValueCell.create(fillSerial(new Float32Array(groupCount))),
+        ...color,
+        ...size,
+        ...marker,
+        ...transforms,
+        ...createPointValues(props, counts)
+    }
+}
+
+export async function createUnitsPointValues(ctx: RuntimeContext, group: Unit.SymmetryGroup, vertices: ValueCell<Float32Array>, locationIt: LocationIterator, props: StructurePointProps): Promise<PointValues> {
+    const transforms = createTransforms(group)
+    return _createPointValues(ctx, transforms, vertices, locationIt, props)
+}
+
+export async function createUnitsPointRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, vertices: ValueCell<Float32Array>, locationIt: LocationIterator, props: StructurePointProps) {
+    const values = await createUnitsPointValues(ctx, group, vertices, locationIt, props)
+    const state = createRenderableState(props)
+    return createPointRenderObject(values, state)
 }
\ No newline at end of file
diff --git a/src/mol-geo/representation/util.ts b/src/mol-geo/representation/util.ts
index 3f2614201..34b2dca08 100644
--- a/src/mol-geo/representation/util.ts
+++ b/src/mol-geo/representation/util.ts
@@ -29,6 +29,12 @@ export const DefaultMeshProps = {
 }
 export type MeshProps = typeof DefaultMeshProps
 
+export const DefaultPointProps = {
+    ...DefaultBaseProps,
+    pointSizeAttenuation: true
+}
+export type PointProps = typeof DefaultPointProps
+
 export type TransformData = {
     aTransform: ValueCell<Float32Array>,
 }
@@ -54,6 +60,7 @@ export function createBaseValues(props: Required<BaseProps>, counts: Counts) {
         aInstance: ValueCell.create(fillSerial(new Float32Array(counts.instanceCount))),
         drawCount: ValueCell.create(counts.drawCount),
         instanceCount: ValueCell.create(counts.instanceCount),
+        dUseFog: ValueCell.create(props.useFog),
     }
 }
 
@@ -63,7 +70,13 @@ export function createMeshValues(props: Required<MeshProps>, counts: Counts) {
         dDoubleSided: ValueCell.create(props.doubleSided),
         dFlatShaded: ValueCell.create(props.flatShaded),
         dFlipSided: ValueCell.create(props.flipSided),
-        dUseFog: ValueCell.create(props.useFog),
+    }
+}
+
+export function createPointValues(props: Required<PointProps>, counts: Counts) {
+    return {
+        ...createBaseValues(props, counts),
+        dPointSizeAttenuation: ValueCell.create(props.pointSizeAttenuation),
     }
 }
 
@@ -76,6 +89,7 @@ export function createRenderableState(props: Required<BaseProps>): RenderableSta
 
 export function updateBaseValues(values: BaseValues, props: Required<BaseProps>) {
     ValueCell.updateIfChanged(values.uAlpha, props.alpha)
+    ValueCell.updateIfChanged(values.dUseFog, props.useFog)
 }
 
 export function updateMeshValues(values: MeshValues, props: Required<MeshProps>) {
@@ -83,7 +97,6 @@ export function updateMeshValues(values: MeshValues, props: Required<MeshProps>)
     ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
     ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
     ValueCell.updateIfChanged(values.dFlipSided, props.flipSided)
-    ValueCell.updateIfChanged(values.dUseFog, props.useFog)
 }
 
 export function updateRenderableState(state: RenderableState, props: Required<BaseProps>) {
diff --git a/src/mol-geo/util/size-data.ts b/src/mol-geo/util/size-data.ts
index f1a59a8fa..2afbf89ee 100644
--- a/src/mol-geo/util/size-data.ts
+++ b/src/mol-geo/util/size-data.ts
@@ -9,6 +9,7 @@ import { Vec2 } from 'mol-math/linear-algebra';
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 import { LocationIterator } from './location-iterator';
 import { Location, NullLocation } from 'mol-model/location';
+import { RuntimeContext } from 'mol-task';
 
 export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 
@@ -48,7 +49,7 @@ export function createValueSize(value: number, sizeData?: SizeData): SizeData {
 }
 
 /** Creates size uniform */
-export function createUniformSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
+export async function createUniformSize(ctx: RuntimeContext, locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): Promise<SizeData> {
     return createValueSize(sizeFn(NullLocation), sizeData)
 }
 
@@ -72,36 +73,51 @@ export function createTextureSize(sizes: TextureImage, type: SizeType, sizeData?
 }
 
 /** Creates size texture with size for each instance/unit */
-export function createInstanceSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
+export async function createInstanceSize(ctx: RuntimeContext, locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): Promise<SizeData> {
     const { instanceCount} = locationIt
     const sizes = sizeData && sizeData.tSize.ref.value.array.length >= instanceCount ? sizeData.tSize.ref.value : createTextureImage(instanceCount, 1)
+    let i = 0
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
         const v = locationIt.move()
         sizes.array[v.instanceIndex] = sizeFn(v.location)
         locationIt.skipInstance()
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Creating instance sizes', current: i, max: instanceCount });
+        }
+        ++i
     }
     return createTextureSize(sizes, 'instance', sizeData)
 }
 
 /** Creates size texture with size for each group (i.e. shared across instances/units) */
-export function createGroupSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
+export async function createGroupSize(ctx: RuntimeContext, locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): Promise<SizeData> {
     const { groupCount } = locationIt
     const sizes = sizeData && sizeData.tSize.ref.value.array.length >= groupCount ? sizeData.tSize.ref.value : createTextureImage(groupCount, 1)
+    let i = 0
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
         const v = locationIt.move()
         sizes.array[v.groupIndex] = sizeFn(v.location)
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Creating group sizes', current: i, max: groupCount });
+        }
+        ++i
     }
     return createTextureSize(sizes, 'group', sizeData)
 }
 
 /** Creates size texture with size for each group in each instance (i.e. for each unit) */
-export function createGroupInstanceSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
+export async function createGroupInstanceSize(ctx: RuntimeContext, locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): Promise<SizeData> {
     const { groupCount, instanceCount } = locationIt
     const count = instanceCount * groupCount
     const sizes = sizeData && sizeData.tSize.ref.value.array.length >= count ? sizeData.tSize.ref.value : createTextureImage(count, 1)
+    let i = 0
     while (locationIt.hasNext && !locationIt.isNextNewInstance) {
         const v = locationIt.move()
         sizes.array[v.index] = sizeFn(v.location)
+        if (i % 10000 === 0 && ctx.shouldUpdate) {
+            await ctx.update({ message: 'Creating group instance sizes', current: i, max: count });
+        }
+        ++i
     }
     return createTextureSize(sizes, 'groupInstance', sizeData)
 }
\ No newline at end of file
-- 
GitLab