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