diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index e4acd4e8e3deea2217ac17b790f826bd74becdcb..ea70b750cea30a16ecb9542610fe87a8f0679951 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -68,7 +68,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> const active: { [k: string]: boolean } = { cartoon: true, point: false, - surface: false, + surface: true, ballAndStick: false, carbohydrate: false, spacefill: false, @@ -209,7 +209,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> console.log('createStructureRepr') for (const k in structureRepresentations) { if (active[k]) { - await structureRepresentations[k].createOrUpdate({}, structure).run( + await structureRepresentations[k].createOrUpdate({ colorTheme: { name: 'element-index' } }, structure).run( // progress => console.log(Progress.format(progress)) ) viewer.add(structureRepresentations[k]) diff --git a/src/mol-geo/geometry/mesh/mesh.ts b/src/mol-geo/geometry/mesh/mesh.ts index 255cff2ccf44657d55ebec116ba4e714aef29805..c71247c59338ef274f86a26d5db31e0376bd688d 100644 --- a/src/mol-geo/geometry/mesh/mesh.ts +++ b/src/mol-geo/geometry/mesh/mesh.ts @@ -15,6 +15,7 @@ import { createMarkers } from '../marker-data'; import { TransformData } from '../transform-data'; import { LocationIterator } from '../../util/location-iterator'; import { createColors } from '../color-data'; +import { ChunkedArray } from 'mol-data/util'; export interface Mesh { readonly kind: 'mesh', @@ -160,6 +161,71 @@ export namespace Mesh { }); } + /** + * Ensure that each vertices of each triangle have the same group id. + * Note that normals are copied over and can't be re-created from the new mesh. + */ + export function uniformTriangleGroup(mesh: Mesh) { + const { indexBuffer, vertexBuffer, groupBuffer, normalBuffer, triangleCount, vertexCount } = mesh + const ib = indexBuffer.ref.value + const vb = vertexBuffer.ref.value + const gb = groupBuffer.ref.value + const nb = normalBuffer.ref.value + + // new + const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount) + + // re-use + const vertex = ChunkedArray.create(Float32Array, 3, 1024, vb) + vertex.currentIndex = vertexCount * 3 + vertex.elementCount = vertexCount + const normal = ChunkedArray.create(Float32Array, 3, 1024, nb) + normal.currentIndex = vertexCount * 3 + normal.elementCount = vertexCount + const group = ChunkedArray.create(Float32Array, 1, 1024, gb) + group.currentIndex = vertexCount + group.elementCount = vertexCount + + const v = Vec3.zero() + const n = Vec3.zero() + + function add(i: number) { + Vec3.fromArray(v, vb, i * 3) + Vec3.fromArray(n, nb, i * 3) + ChunkedArray.add3(vertex, v[0], v[1], v[2]) + ChunkedArray.add3(normal, n[0], n[1], n[2]) + } + + let newVertexCount = vertexCount + for (let i = 0, il = triangleCount; i < il; ++i) { + const i0 = ib[i * 3], i1 = ib[i * 3 + 1], i2 = ib[i * 3 + 2] + const g0 = gb[i0], g1 = gb[i1], g2 = gb[i2] + if (g0 !== g1 || g0 !== g2) { + add(i0); add(i1); add(i2) + ChunkedArray.add3(index, newVertexCount, newVertexCount + 1, newVertexCount + 2) + const g = g1 === g2 ? g1 : g0 + for (let j = 0; j < 3; ++j) ChunkedArray.add(group, g) + newVertexCount += 3 + } else { + ChunkedArray.add3(index, i0, i1, i2) + } + } + + const newIb = ChunkedArray.compact(index) + const newVb = ChunkedArray.compact(vertex) + const newNb = ChunkedArray.compact(normal) + const newGb = ChunkedArray.compact(group) + + mesh.vertexCount = newVertexCount + + ValueCell.update(vertexBuffer, newVb) as ValueCell<Float32Array> + ValueCell.update(groupBuffer, newGb) as ValueCell<Float32Array> + ValueCell.update(indexBuffer, newIb) as ValueCell<Uint32Array> + ValueCell.update(normalBuffer, newNb) as ValueCell<Float32Array> + + return mesh + } + // export const DefaultProps = { diff --git a/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts b/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts index 6c9cb6e048cd2d63026a5b067988d556303fd102..f4fabf6403bca4f58835e056d5b87b415bdce192 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts +++ b/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts @@ -15,17 +15,24 @@ import { computeGaussianDensity, DefaultGaussianDensityProps } from './util/gaus async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianSurfaceProps, mesh?: Mesh): Promise<Mesh> { const { smoothness } = props - const { transform, field } = await computeGaussianDensity(unit, structure, props).runAsChild(ctx) + const { transform, field, idField } = await computeGaussianDensity(unit, structure, props).runAsChild(ctx) + console.time('mc') const surface = await computeMarchingCubes({ isoLevel: Math.exp(-smoothness), scalarField: field, + idField, oldSurface: mesh }).runAsChild(ctx) + console.timeEnd('mc') Mesh.transformImmediate(surface, transform) Mesh.computeNormalsImmediate(surface) + console.time('uniformTriangleGroup') + Mesh.uniformTriangleGroup(surface) + console.timeEnd('uniformTriangleGroup') + return surface; } diff --git a/src/mol-geo/representation/structure/visual/util/gaussian.ts b/src/mol-geo/representation/structure/visual/util/gaussian.ts index 20505c1f1573567723ed0b1ef8b5813e82a3ee4f..348bab5c29a94f10ae45f6d4a165e3aa572c276a 100644 --- a/src/mol-geo/representation/structure/visual/util/gaussian.ts +++ b/src/mol-geo/representation/structure/visual/util/gaussian.ts @@ -11,7 +11,7 @@ import { Box3D } from 'mol-math/geometry'; import { SizeTheme } from 'mol-view/theme/size'; export const DefaultGaussianDensityProps = { - resolutionFactor: 7, + resolutionFactor: 6, radiusOffset: 0, smoothness: 1.5, } @@ -28,7 +28,7 @@ function getDelta(box: Box3D, resolutionFactor: number) { return delta } -type Density = { transform: Mat4, field: Tensor } +type Density = { transform: Mat4, field: Tensor, idField: Tensor } export function computeGaussianDensity(unit: Unit, structure: Structure, props: GaussianDensityProps) { return Task.create('Gaussian Density', async ctx => { @@ -74,6 +74,7 @@ export async function GaussianDensity(ctx: RuntimeContext, unit: Unit, structure const beg = Vec3.zero() const end = Vec3.zero() + console.time('density grid') for (let i = 0; i < elementCount; i++) { l.element = elements[i] pos(elements[i], v) @@ -109,13 +110,59 @@ export async function GaussianDensity(ctx: RuntimeContext, unit: Unit, structure await ctx.update({ message: 'filling density grid', current: i, max: elementCount }); } } + console.timeEnd('density grid') const t = Mat4.identity() Mat4.fromScaling(t, Vec3.inverse(Vec3.zero(), delta)) Mat4.setTranslation(t, expandedBox.min) + const { dimensions, get } = space + const [ xn, yn, zn ] = dimensions + + const n = xn * yn * zn + const idData = space.create() + const idField = Tensor.create(space, idData) + + const lookup3d = unit.lookup3d + + let i = 0 + + // TODO use max radius of radiusTheme instead of 4 + const _rSq = 4 + 1 / Math.max(...delta) + const _minDsq = _rSq * 4 + + console.time('id grid') + for (let x = 0; x < xn; ++x) { + for (let y = 0; y < yn; ++y) { + for (let z = 0; z < zn; ++z) { + const dens = get(data, x, y, z) + let group = -1 + if (dens > 0 && dens < 2) { + Vec3.set(p, x, y, z) + Vec3.transformMat4(p, p, t) + const r = lookup3d.find(p[0], p[1], p[2], _rSq) + let minDsq = _minDsq + for (let j = 0, jl = r.count; j < jl; ++j) { + const dSq = r.squaredDistances[j] + if (dSq < minDsq) { + minDsq = dSq + group = r.indices[j] + } + } + } + space.set(idData, x, y, z, group) + if (i % 1000000 === 0 && ctx.shouldUpdate) { + await ctx.update({ message: 'filling id grid', current: i, max: n }); + } + ++i + } + } + } + console.timeEnd('id grid') + return { field, + idField, transform: t } } \ No newline at end of file diff --git a/src/mol-math/geometry/lookup3d/grid.ts b/src/mol-math/geometry/lookup3d/grid.ts index e91b0aff68af67792644b4ce9f4348b9186d0fb5..6fa09806898f2d7122b88a673613ca356d17a4a6 100644 --- a/src/mol-math/geometry/lookup3d/grid.ts +++ b/src/mol-math/geometry/lookup3d/grid.ts @@ -212,7 +212,7 @@ function build(data: PositionData, cellSize?: Vec3) { } interface QueryContext { - structure: Grid3D, + grid: Grid3D, x: number, y: number, z: number, @@ -221,12 +221,12 @@ interface QueryContext { isCheck: boolean } -function createContext(structure: Grid3D): QueryContext { - return { structure, x: 0.1, y: 0.1, z: 0.1, radius: 0.1, result: Result.create(), isCheck: false } +function createContext(grid: Grid3D): QueryContext { + return { grid, x: 0.1, y: 0.1, z: 0.1, radius: 0.1, result: Result.create(), isCheck: false } } function query(ctx: QueryContext): boolean { - const { min, size: [sX, sY, sZ], bucketOffset, bucketCounts, bucketArray, grid, data: { x: px, y: py, z: pz, indices, radius }, delta, maxRadius } = ctx.structure; + const { min, size: [sX, sY, sZ], bucketOffset, bucketCounts, bucketArray, grid, data: { x: px, y: py, z: pz, indices, radius }, delta, maxRadius } = ctx.grid; const { radius: inputRadius, isCheck, x, y, z, result } = ctx; const r = inputRadius + maxRadius;