diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx index bdc194b701b259a28ff093d15372dcc861b75672..497fa6e8b324d631f44afe2dfe45bf4bd3e5347f 100644 --- a/src/apps/canvas/component/structure-representation.tsx +++ b/src/apps/canvas/component/structure-representation.tsx @@ -10,6 +10,7 @@ import Viewer from 'mol-view/viewer'; import { VisualQuality, VisualQualityNames } from 'mol-geo/representation/util'; import { ColorThemeProps, ColorThemeName, ColorThemeNames, ColorTheme } from 'mol-view/theme/color'; import { Color } from 'mol-util/color'; +import { Progress } from 'mol-task'; export interface StructureRepresentationComponentProps { viewer: Viewer @@ -22,6 +23,10 @@ export interface StructureRepresentationComponentState { alpha: number quality: VisualQuality colorTheme: ColorThemeProps + + resolutionFactor?: number + probeRadius?: number + isoValue?: number } export class StructureRepresentationComponent extends React.Component<StructureRepresentationComponentProps, StructureRepresentationComponentState> { @@ -31,6 +36,10 @@ export class StructureRepresentationComponent extends React.Component<StructureR alpha: this.props.representation.props.alpha, quality: this.props.representation.props.quality, colorTheme: this.props.representation.props.colorTheme, + + resolutionFactor: (this.props.representation.props as any).resolutionFactor, + probeRadius: (this.props.representation.props as any).probeRadius, + isoValue: (this.props.representation.props as any).isoValue, } componentWillMount() { @@ -43,6 +52,10 @@ export class StructureRepresentationComponent extends React.Component<StructureR alpha: repr.props.alpha, quality: repr.props.quality, colorTheme: repr.props.colorTheme, + + resolutionFactor: (repr.props as any).resolutionFactor, + probeRadius: (repr.props as any).probeRadius, + isoValue: (repr.props as any).isoValue, }) } @@ -55,17 +68,36 @@ export class StructureRepresentationComponent extends React.Component<StructureR if (state.alpha !== undefined) props.alpha = state.alpha if (state.colorTheme !== undefined) props.colorTheme = state.colorTheme - await repr.createOrUpdate(props).run() + if (state.resolutionFactor !== undefined) (props as any).resolutionFactor = state.resolutionFactor + if (state.probeRadius !== undefined) (props as any).probeRadius = state.probeRadius + if (state.isoValue !== undefined) (props as any).isoValue = state.isoValue + + await repr.createOrUpdate(props).run( + progress => console.log(Progress.format(progress)), 100 + ) this.props.viewer.add(repr) - this.props.viewer.requestDraw(true) + this.props.viewer.draw(true) console.log(this.props.viewer.stats) + console.log( + 'drawCount', + repr.renderObjects[0].values.drawCount.ref.version, + repr.renderObjects[0].values.drawCount.ref.value, + 'dColorType', + repr.renderObjects[0].values.dColorType.ref.version, + repr.renderObjects[0].values.dColorType.ref.value + ) + const newState = { ...this.state, visible: repr.props.visible, quality: repr.props.quality, alpha: repr.props.alpha, colorTheme: repr.props.colorTheme, + + resolutionFactor: (repr.props as any).resolutionFactor, + probeRadius: (repr.props as any).probeRadius, + isoValue: (repr.props as any).isoValue, } this.setState(newState) } @@ -107,6 +139,39 @@ export class StructureRepresentationComponent extends React.Component<StructureR > </input> </div> + { this.state.resolutionFactor !== undefined ? <div> + <span>Resolution Factor </span> + <input type='range' + defaultValue={this.state.resolutionFactor.toString()} + min='4' + max='9' + step='1' + onInput={(e) => this.update({ resolutionFactor: parseInt(e.currentTarget.value) })} + > + </input> + </div> : '' } + { this.state.isoValue !== undefined ? <div> + <span>Iso Value </span> + <input type='range' + defaultValue={this.state.isoValue.toString()} + min='0.1' + max='2' + step='0.1' + onInput={(e) => this.update({ isoValue: parseFloat(e.currentTarget.value) })} + > + </input> + </div> : '' } + { this.state.probeRadius !== undefined ? <div> + <span>Probe Radius </span> + <input type='range' + defaultValue={this.state.probeRadius.toString()} + min='0' + max='10' + step='0.1' + onInput={(e) => this.update({ probeRadius: parseFloat(e.currentTarget.value) })} + > + </input> + </div> : '' } <div> <span>Color Theme </span> <select value={colorTheme.name} onChange={(e) => this.update({ colorTheme: { name: e.target.value as ColorThemeName } }) }> diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index c5674578f27d99dae9056db73f63464279da2c44..a4fe44feed193911127e579e17b324b87b39e1cc 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -25,6 +25,7 @@ import { BehaviorSubject } from 'rxjs'; import { SpacefillRepresentation } from 'mol-geo/representation/structure/representation/spacefill'; import { DistanceRestraintRepresentation } from 'mol-geo/representation/structure/representation/distance-restraint'; import { SurfaceRepresentation } from 'mol-geo/representation/structure/representation/surface'; +import { Progress } from 'mol-task'; export interface StructureView { readonly viewer: Viewer @@ -67,7 +68,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> const active: { [k: string]: boolean } = { cartoon: true, point: false, - surface: true, + surface: false, ballAndStick: false, carbohydrate: false, spacefill: false, @@ -208,7 +209,9 @@ 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({}, structure).run( + progress => console.log(Progress.format(progress)), 100 + ) viewer.add(structureRepresentations[k]) } else { viewer.remove(structureRepresentations[k]) @@ -247,7 +250,7 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> updated.next(null) viewer.requestDraw(true) - console.log(viewer.stats) + console.log('stats', viewer.stats) } async function createSymmetryRepr() { diff --git a/src/mol-geo/representation/structure/complex-representation.ts b/src/mol-geo/representation/structure/complex-representation.ts index 3f624d4af188435dccb9238decbf223f9de2665c..66d65d10272ef0da7794495e7899c89f7c45d875 100644 --- a/src/mol-geo/representation/structure/complex-representation.ts +++ b/src/mol-geo/representation/structure/complex-representation.ts @@ -20,7 +20,7 @@ export function ComplexRepresentation<P extends StructureProps>(label: string, v function createOrUpdate(props: Partial<P> = {}, structure?: Structure) { _props = Object.assign({}, _props, props) - return Task.create('Creating StructureRepresentation', async ctx => { + return Task.create('Creating or updating ComplexRepresentation', async ctx => { if (!visual) visual = visualCtor() await visual.createOrUpdate(ctx, _props, structure) }); diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts index a98620a7f37577c8370be896af55bbbc0213c6a0..0644d5bbf19dfa1f510a77d245df5b0ec2eeef3a 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/src/mol-geo/representation/structure/units-visual.ts @@ -126,14 +126,19 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu if (!group && !currentGroup) { throw new Error('missing group') } else if (group && (!currentGroup || !renderObject)) { + // console.log('unit-visual first create') await create(ctx, group, props) } else if (group && group.hashCode !== currentGroup.hashCode) { + // console.log('unit-visual group.hashCode !== currentGroup.hashCode') await create(ctx, group, props) } else { - if (group && !areGroupsIdentical(group, currentGroup)) { + // console.log('unit-visual update') + if (group && !sameGroupConformation(group, currentGroup)) { + // console.log('unit-visual new conformation') currentGroup = group + } else { + await update(ctx, props) } - await update(ctx, props) } }, getLoci(pickingId: PickingId) { @@ -168,7 +173,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu } } -function areGroupsIdentical(groupA: Unit.SymmetryGroup, groupB: Unit.SymmetryGroup) { +function sameGroupConformation(groupA: Unit.SymmetryGroup, groupB: Unit.SymmetryGroup) { return ( groupA.units.length === groupB.units.length && Unit.conformationId(groupA.units[0]) === Unit.conformationId(groupB.units[0]) 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 7176fe48f39a07687b107da400ac16753521c6c0..ec42e86a277c57daf381941e0c2a0d2e7bd30432 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts +++ b/src/mol-geo/representation/structure/visual/gaussian-surface-mesh.ts @@ -13,18 +13,21 @@ import { StructureElementIterator, getElementLoci, markElement } from './util/el import { computeMarchingCubes } from '../../../util/marching-cubes/algorithm'; import { Tensor, Vec3, Mat4 } from 'mol-math/linear-algebra'; import { Box3D } from 'mol-math/geometry'; -import { ValueCell } from 'mol-util'; import { smoothstep } from 'mol-math/interpolate'; -import { LocationIterator } from '../../../util/location-iterator'; +import { SizeThemeProps, SizeTheme } from 'mol-view/theme/size'; export interface GaussianSurfaceMeshProps { + sizeTheme: SizeThemeProps + resolutionFactor: number + probeRadius: number + isoValue: number } -function getDelta(box: Box3D) { +function getDelta(box: Box3D, resolutionFactor: number) { const extent = Vec3.sub(Vec3.zero(), box.max, box.min) - const n = Math.pow(128, 3) + const n = Math.pow(Math.pow(2, resolutionFactor), 3) const f = (extent[0] * extent[1] * extent[2]) / n const s = Math.pow(f, 1 / 3) const size = Vec3.zero() @@ -35,17 +38,21 @@ function getDelta(box: Box3D) { } async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianSurfaceMeshProps, mesh?: Mesh): Promise<Mesh> { + const { resolutionFactor, probeRadius, isoValue } = props const { elements } = unit; const elementCount = elements.length; - - const r = 2.5; + const sizeTheme = SizeTheme(props.sizeTheme) const v = Vec3.zero() const p = Vec3.zero() const pos = unit.conformation.invariantPosition + const l = StructureElement.create() + l.unit = unit + + const pad = (probeRadius + 2) * 2 // TODO calculate max radius const box = unit.lookup3d.boundary.box - const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(r*3, r*3, r*3)); + const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad)); const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min) const min = expandedBox.min @@ -63,7 +70,7 @@ async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, struct // console.log('s', s) // console.log('size', size) // console.log('delta', delta) - const delta = getDelta(Box3D.expand(Box3D.empty(), structure.boundary.box, Vec3.create(r*3, r*3, r*3))) + const delta = getDelta(Box3D.expand(Box3D.empty(), structure.boundary.box, Vec3.create(pad, pad, pad)), resolutionFactor) const dim = Vec3.zero() Vec3.ceil(dim, Vec3.mul(dim, extent, delta)) // console.log('dim', dim, dim[0] * dim[1] * dim[2]) @@ -73,11 +80,12 @@ async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, struct const field = Tensor.create(space, data) for (let i = 0; i < elementCount; i++) { + l.element = elements[i] pos(elements[i], v) Vec3.mul(v, Vec3.sub(v, v, min), delta) - const size = r + const size = sizeTheme.size(l) + probeRadius const radius = size * delta[0] const minX = Math.floor(v[0] - radius) @@ -107,11 +115,10 @@ async function createGaussianSurfaceMesh(ctx: RuntimeContext, unit: Unit, struct // console.log('data', data) const surface = await computeMarchingCubes({ - isoLevel: 0.1, + isoLevel: isoValue, scalarField: field, oldSurface: mesh - - }).runAsChild(ctx); + }).runAsChild(ctx) const t = Mat4.identity() Mat4.fromUniformScaling(t, 1 / delta[0]) @@ -132,6 +139,10 @@ export const DefaultGaussianSurfaceProps = { flipSided: true, // flatShaded: true, + + resolutionFactor: 7, + probeRadius: 1.4, + isoValue: 0.1, } export type GaussianSurfaceProps = typeof DefaultGaussianSurfaceProps @@ -142,19 +153,10 @@ export function GaussianSurfaceVisual(): UnitsVisual<GaussianSurfaceProps> { createLocationIterator: StructureElementIterator.fromGroup, getLoci: getElementLoci, mark: markElement, - setUpdateState: (state: MeshUpdateState, newProps: GaussianSurfaceProps, currentProps: GaussianSurfaceProps) => {} + setUpdateState: (state: MeshUpdateState, newProps: GaussianSurfaceProps, currentProps: GaussianSurfaceProps) => { + if (newProps.resolutionFactor !== currentProps.resolutionFactor) state.createMesh = true + if (newProps.probeRadius !== currentProps.probeRadius) state.createMesh = true + if (newProps.isoValue !== currentProps.isoValue) state.createMesh = true + } }) -} - -// function SingleGroupLocationIterator(group: Unit.SymmetryGroup): LocationIterator { -// const groupCount = 1 -// const instanceCount = group.units.length -// const location = StructureElement.create() -// const getLocation = (groupIndex: number, instanceIndex: number) => { -// const unit = group.units[instanceIndex] -// location.unit = unit -// location.element = unit.elements[groupIndex] -// return location -// } -// return LocationIterator(groupCount, instanceCount, getLocation) -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-geo/util/marching-cubes/algorithm.ts b/src/mol-geo/util/marching-cubes/algorithm.ts index 32ee9549a7f82a4f165bac0411bb03b11d2f70f3..651b823e02ef7f383cc070bac8bd8e8231b3b336 100644 --- a/src/mol-geo/util/marching-cubes/algorithm.ts +++ b/src/mol-geo/util/marching-cubes/algorithm.ts @@ -28,7 +28,7 @@ export interface MarchingCubesParameters { export function computeMarchingCubes(parameters: MarchingCubesParameters) { return Task.create('Marching Cubes', async ctx => { - let comp = new MarchingCubesComputation(parameters, ctx); + const comp = new MarchingCubesComputation(parameters, ctx); return await comp.run(); }); } @@ -64,17 +64,18 @@ class MarchingCubesComputation { this.state.clearEdgeVertexIndexSlice(k); } - private finish() { + private finish(): Mesh { const vb = ChunkedArray.compact(this.state.vertexBuffer, true) as Float32Array; const ib = ChunkedArray.compact(this.state.triangleBuffer, true) as Uint32Array; const gb = ChunkedArray.compact(this.state.groupBuffer, true) as Float32Array; this.state.vertexBuffer = <any>void 0; this.state.verticesOnEdges = <any>void 0; + this.state.groupBuffer = <any>void 0; const os = this.parameters.oldSurface - const ret: Mesh = { + return { vertexCount: this.state.vertexCount, triangleCount: this.state.triangleCount, vertexBuffer: os ? ValueCell.update(os.vertexBuffer, vb) : ValueCell.create(vb), @@ -83,8 +84,6 @@ class MarchingCubesComputation { normalBuffer: os ? os.normalBuffer : ValueCell.create(new Float32Array(0)), normalsComputed: false } - - return ret; } async run() { @@ -94,11 +93,8 @@ class MarchingCubesComputation { return this.finish(); } - constructor( - parameters: MarchingCubesParameters, - private ctx: RuntimeContext) { - - let params = { ...parameters }; + constructor(parameters: MarchingCubesParameters, private ctx: RuntimeContext) { + const params = { ...parameters }; this.parameters = params; if (!params.bottomLeft) params.bottomLeft = [0, 0, 0]; @@ -148,8 +144,8 @@ class MarchingCubesState { } private interpolate(edgeNum: number) { - const info = EdgeIdInfo[edgeNum], - edgeId = 3 * this.get3dOffsetFromEdgeInfo(info) + info.e; + const info = EdgeIdInfo[edgeNum]; + const edgeId = 3 * this.get3dOffsetFromEdgeInfo(info) + info.e; const ret = this.verticesOnEdges[edgeId]; if (ret > 0) return (ret - 1) | 0; @@ -247,7 +243,7 @@ class MarchingCubesState { if ((edgeInfo & 1024) > 0) this.vertList[10] = this.interpolate(10); // 2 6 if ((edgeInfo & 2048) > 0) this.vertList[11] = this.interpolate(11); // 3 7 - let triInfo = TriTable[tableIndex]; + const triInfo = TriTable[tableIndex]; for (let t = 0; t < triInfo.length; t += 3) { this.triangleCount++; ChunkedArray.add3(this.triangleBuffer, this.vertList[triInfo[t]], this.vertList[triInfo[t + 1]], this.vertList[triInfo[t + 2]]);