Browse Source

Merge branch 'master' into split-plugin-context

dsehnal 4 years ago
parent
commit
e3175c3ed1

+ 4 - 4
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "2.0.0-dev.4",
+  "version": "2.0.0-dev.7",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -115,7 +115,7 @@
     "simple-git": "^2.25.0",
     "style-loader": "^2.0.0",
     "ts-jest": "^26.4.4",
-    "typescript": "^4.1.2",
+    "typescript": "^4.2.3",
     "webpack": "^4.44.1",
     "webpack-cli": "^3.3.12",
     "webpack-version-file-plugin": "^0.4.0"
@@ -137,12 +137,12 @@
     "cors": "^2.8.5",
     "express": "^4.17.1",
     "h264-mp4-encoder": "^1.0.12",
-    "immer": "^8.0.0",
+    "immer": "^8.0.1",
     "immutable": "^3.8.2",
     "node-fetch": "^2.6.1",
     "react": "^17.0.1",
     "react-dom": "^17.0.1",
-    "rxjs": "^6.6.3",
+    "rxjs": "^6.6.6",
     "swagger-ui-dist": "^3.37.2",
     "tslib": "^2.1.0",
     "util.promisify": "^1.0.1",

+ 0 - 6
src/apps/viewer/index.html

@@ -48,16 +48,10 @@
             var debugMode = getParam('debug-mode', '[^&]+').trim() === '1';
             if (debugMode) molstar.setDebugMode(debugMode, debugMode);
 
-            var disableAntialiasing = getParam('disable-antialiasing', '[^&]+').trim() === '1';
-            var pixelScale = parseFloat(getParam('pixel-scale', '[^&]+').trim() || '1');
-            var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
             var hideControls = getParam('hide-controls', '[^&]+').trim() === '1';
             var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase();
             var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase();
             var viewer = new molstar.Viewer('app', {
-                disableAntialiasing: disableAntialiasing,
-                pixelScale: pixelScale,
-                enableWboit: !disableWboit,
                 layoutShowControls: !hideControls,
                 viewportShowExpand: false,
                 pdbProvider: pdbProvider || 'pdbe',

+ 4 - 4
src/mol-canvas3d/passes/draw.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -100,7 +100,7 @@ export class DrawPass {
     }
 
     constructor(private webgl: WebGLContext, width: number, height: number, enableWboit: boolean) {
-        const { extensions, resources } = webgl;
+        const { extensions, resources, isWebGL2 } = webgl;
 
         this.drawTarget = createNullRenderTarget(webgl.gl);
 
@@ -113,8 +113,8 @@ export class DrawPass {
         this.depthTargetPrimitives = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
         this.depthTargetVolumes = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
 
-        this.depthTexturePrimitives = this.depthTargetPrimitives ? this.depthTargetPrimitives.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest');
-        this.depthTextureVolumes = this.depthTargetVolumes ? this.depthTargetVolumes.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest');
+        this.depthTexturePrimitives = this.depthTargetPrimitives ? this.depthTargetPrimitives.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
+        this.depthTextureVolumes = this.depthTargetVolumes ? this.depthTargetVolumes.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
         if (!this.packedDepth) {
             this.depthTexturePrimitives.define(width, height);
             this.depthTextureVolumes.define(width, height);

+ 6 - 4
src/mol-gl/webgl/render-target.ts

@@ -8,7 +8,7 @@ import { idFactory } from '../../mol-util/id-factory';
 import { createNullTexture, Texture, TextureFilter } from './texture';
 import { createNullFramebuffer, Framebuffer } from './framebuffer';
 import { WebGLResources } from './resources';
-import { GLRenderingContext } from './compat';
+import { GLRenderingContext, isWebGL2 } from './compat';
 
 const getNextRenderTargetId = idFactory();
 
@@ -35,9 +35,11 @@ export function createRenderTarget(gl: GLRenderingContext, resources: WebGLResou
             ? resources.texture('image-float32', 'rgba', 'float', filter)
             : resources.texture('image-uint8', 'rgba', 'ubyte', filter);
     // make a depth renderbuffer of the same size as the targetTexture
-    const depthRenderbuffer = depth
-        ? resources.renderbuffer('depth16', 'depth', _width, _height)
-        : null;
+    const depthRenderbuffer = !depth
+        ? null
+        : isWebGL2(gl)
+            ? resources.renderbuffer('depth32f', 'depth', _width, _height)
+            : resources.renderbuffer('depth16', 'depth', _width, _height);
 
     function init() {
         targetTexture.define(_width, _height);

+ 6 - 3
src/mol-gl/webgl/renderbuffer.ts

@@ -1,17 +1,17 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { idFactory } from '../../mol-util/id-factory';
-import { GLRenderingContext } from './compat';
+import { GLRenderingContext, isWebGL2 } from './compat';
 import { Framebuffer, checkFramebufferStatus } from './framebuffer';
 import { isDebugMode } from '../../mol-util/debug';
 
 const getNextRenderbufferId = idFactory();
 
-export type RenderbufferFormat = 'depth16' | 'stencil8' | 'rgba4' | 'depth-stencil'
+export type RenderbufferFormat = 'depth16' | 'stencil8' | 'rgba4' | 'depth-stencil' | 'depth32f'
 export type RenderbufferAttachment = 'depth' | 'stencil' | 'depth-stencil' | 'color0'
 
 export function getFormat(gl: GLRenderingContext, format: RenderbufferFormat) {
@@ -20,6 +20,9 @@ export function getFormat(gl: GLRenderingContext, format: RenderbufferFormat) {
         case 'stencil8': return gl.STENCIL_INDEX8;
         case 'rgba4': return gl.RGBA4;
         case 'depth-stencil': return gl.DEPTH_STENCIL;
+        case 'depth32f':
+            if (isWebGL2(gl)) return gl.DEPTH_COMPONENT32F;
+            else throw new Error('WebGL2 needed for `depth32f` renderbuffer format');
     }
 }
 

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

@@ -94,7 +94,10 @@ export function getInternalFormat(gl: GLRenderingContext, format: TextureFormat,
                     case 'int': return gl.RGBA32I;
                 }
             case 'depth':
-                return gl.DEPTH_COMPONENT16;
+                switch (type) {
+                    case 'ushort': return gl.DEPTH_COMPONENT16;
+                    case 'float': return gl.DEPTH_COMPONENT32F;
+                }
         }
     }
     return getFormat(gl, format, type);
@@ -229,7 +232,7 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
         (kind.endsWith('float16') && _type !== 'fp16') ||
         (kind.endsWith('uint8') && _type !== 'ubyte') ||
         (kind.endsWith('int32') && _type !== 'int') ||
-        (kind.endsWith('depth') && _type !== 'ushort')
+        (kind.endsWith('depth') && _type !== 'ushort' && _type !== 'float')
     ) {
         throw new Error(`texture kind '${kind}' and type '${_type}' are incompatible`);
     }

+ 1 - 1
src/mol-model-formats/structure/basic/atomic.ts

@@ -74,7 +74,7 @@ function createHierarchyData(atom_site: AtomSite, sourceIndex: Column<number>, o
         let cI = 0;
         let seqId = 0;
         for (let i = 0, il = seqIds.length; i < il; ++i) {
-            if (residueOffsets[i] > chainOffsets[cI + 1]) {
+            if (residueOffsets[i] >= chainOffsets[cI + 1]) {
                 cI += 1;
                 seqId = 0;
             }

+ 3 - 3
src/mol-model-formats/structure/xyz.ts

@@ -84,7 +84,7 @@ function getModels(mol: XyzFile, ctx: RuntimeContext) {
         atom_site
     });
 
-    return createModels(basics, MolFormat.create(mol), ctx);
+    return createModels(basics, XyzFormat.create(mol), ctx);
 }
 
 //
@@ -93,9 +93,9 @@ export { XyzFormat };
 
 type XyzFormat = ModelFormat<XyzFile>
 
-namespace MolFormat {
+namespace XyzFormat {
     export function is(x?: ModelFormat): x is XyzFormat {
-        return x?.kind === 'mol';
+        return x?.kind === 'xyz';
     }
 
     export function create(mol: XyzFile): XyzFormat {

+ 1 - 1
src/mol-model-props/computed/interactions.ts

@@ -28,7 +28,7 @@ export const InteractionsProvider: CustomStructureProperty.Provider<Interactions
     type: 'local',
     defaultParams: InteractionsParams,
     getParams: (data: Structure) => InteractionsParams,
-    isApplicable: (data: Structure) => true,
+    isApplicable: (data: Structure) => !data.isCoarseGrained,
     obtain: async (ctx: CustomProperty.Context, data: Structure, props: Partial<InteractionsProps>) => {
         const p = { ...PD.getDefaultValues(InteractionsParams), ...props };
         return { value: await computeInteractions(ctx, data, p) };

+ 1 - 1
src/mol-model-props/computed/representations/interactions.ts

@@ -46,7 +46,7 @@ export const InteractionsRepresentationProvider = StructureRepresentationProvide
     defaultValues: PD.getDefaultValues(InteractionsParams),
     defaultColorTheme: { name: 'interaction-type' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (structure: Structure) => structure.elementCount > 0,
+    isApplicable: (structure: Structure) => structure.elementCount > 0 && InteractionsProvider.isApplicable(structure),
     ensureCustomProperties: {
         attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true),
         detach: (data) => InteractionsProvider.ref(data, false)

+ 1 - 1
src/mol-model-props/computed/secondary-structure.ts

@@ -69,7 +69,7 @@ async function computeDssp(structure: Structure, props: DSSPComputationProps): P
     const map = new Map<number, SecondaryStructure>();
     for (let i = 0, il = structure.unitSymmetryGroups.length; i < il; ++i) {
         const u = structure.unitSymmetryGroups[i].units[0];
-        if (Unit.isAtomic(u)) {
+        if (Unit.isAtomic(u) && !Model.isCoarseGrained(u.model)) {
             const secondaryStructure = await computeUnitDSSP(u, props);
             map.set(u.invariantId, secondaryStructure);
         }

+ 35 - 1
src/mol-model/structure/model/model.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -29,6 +29,7 @@ import { CustomModelProperty } from '../../../mol-model-props/common/custom-mode
 import { Trajectory, ArrayTrajectory } from '../trajectory';
 import { Unit } from '../structure';
 import { SortedArray } from '../../../mol-data/int/sorted-array';
+import { PolymerType } from './types';
 
 /**
  * Interface to the "source data" of the molecule.
@@ -224,6 +225,39 @@ export namespace Model {
         }
     };
 
+    const CoarseGrainedProp = '__CoarseGrained__';
+    /**
+     * Has typical coarse grained atom names (BB, SC1) or less than three times as many
+     * atoms as polymer residues (C-alpha only models).
+     */
+    export function isCoarseGrained(model: Model): boolean {
+        if (model._staticPropertyData[CoarseGrainedProp] !== undefined) return model._staticPropertyData[CoarseGrainedProp];
+
+        let polymerResidueCount = 0;
+        const { polymerType } = model.atomicHierarchy.derived.residue;
+        for (let i = 0; i < polymerType.length; ++i) {
+            if (polymerType[i] !== PolymerType.NA) polymerResidueCount += 1;
+        }
+
+        // check for coarse grained atom names
+        let hasBB = false, hasSC1 = false;
+        const { label_atom_id, _rowCount: atomCount } = model.atomicHierarchy.atoms;
+        for (let i = 0; i < atomCount; ++i) {
+            const atomName = label_atom_id.value(i);
+            if (!hasBB && atomName === 'BB') hasBB = true;
+            if (!hasSC1 && atomName === 'SC1') hasSC1 = true;
+            if (hasBB && hasSC1) break;
+        }
+
+        const coarseGrained = (hasBB && hasSC1) || (
+            polymerResidueCount && atomCount
+                ? atomCount / polymerResidueCount < 3
+                : false
+        );
+        model._staticPropertyData[CoarseGrainedProp] = coarseGrained;
+        return coarseGrained;
+    }
+
     //
 
     export function hasCarbohydrate(model: Model): boolean {

+ 1 - 1
src/mol-model/structure/query/utils/structure-distance.ts

@@ -23,7 +23,7 @@ export function checkStructureMaxRadiusDistance(ctx: QueryContext, a: Structure,
 }
 
 namespace MinMaxDist {
-    const enum Result {
+    export const enum Result {
         BelowMin,
         WithinMax,
         Miss

+ 5 - 7
src/mol-model/structure/structure/structure.ts

@@ -158,13 +158,11 @@ class Structure {
     }
 
     /**
-     * Coarse-grained structure, defined as containing less than
-     * twice as many elements as polymer residues
+     * True if any model the structure is based on is coarse grained.
+     * @see Model.isCoarseGrained
      */
     get isCoarseGrained() {
-        const ec = this.elementCount;
-        const prc = this.polymerResidueCount;
-        return prc && ec ? ec / prc < 2 : false;
+        return this.models.some(m => Model.isCoarseGrained(m));
     }
 
     get isEmpty() {
@@ -299,7 +297,7 @@ class Structure {
 
     /** Contains only atomic units */
     get isAtomic() {
-        for (const u of this.units) if (Unit.isAtomic(u)) return false;
+        for (const u of this.units) if (!Unit.isAtomic(u)) return false;
         return true;
     }
 
@@ -311,7 +309,7 @@ class Structure {
 
     /** Contains only coarse units */
     get isCoarse() {
-        for (const u of this.units) if (Unit.isCoarse(u)) return false;
+        for (const u of this.units) if (!Unit.isCoarse(u)) return false;
         return true;
     }
 

+ 1 - 1
src/mol-model/structure/structure/unit/bonds/inter-compute.ts

@@ -176,7 +176,7 @@ export interface InterBondComputationProps extends BondComputationProps {
 function findBonds(structure: Structure, props: InterBondComputationProps) {
     const builder = new InterUnitGraph.Builder<number, StructureElement.UnitIndex, InterUnitEdgeProps>();
 
-    if (props.noCompute) {
+    if (props.noCompute || structure.isCoarseGrained) {
         // TODO add function that only adds bonds defined in structConn and avoids using
         //      structure.lookup and unit.lookup (expensive for large structure and not
         //      needed for archival files or files with an MD topology)

+ 2 - 1
src/mol-model/structure/structure/unit/bonds/intra-compute.ts

@@ -19,6 +19,7 @@ import { StructConn } from '../../../../../mol-model-formats/structure/property/
 import { Vec3 } from '../../../../../mol-math/linear-algebra';
 import { ElementIndex } from '../../../model/indexing';
 import { equalEps } from '../../../../../mol-math/linear-algebra/3d/common';
+import { Model } from '../../../model/model';
 
 function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], atomCount: number, canRemap: boolean): IntraUnitBonds {
     const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB);
@@ -233,7 +234,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
 
 function computeIntraUnitBonds(unit: Unit.Atomic, props?: Partial<BondComputationProps>) {
     const p = { ...DefaultBondComputationProps, ...props };
-    if (p.noCompute) {
+    if (p.noCompute || Model.isCoarseGrained(unit.model)) {
         // TODO add function that only adds bonds defined in structConn of chemCompBond
         //      and avoid using unit.lookup
         return IntraUnitBonds.Empty;

+ 16 - 7
src/mol-plugin-state/builder/structure/representation-preset.ts

@@ -22,6 +22,7 @@ import { ChainIdColorThemeProvider } from '../../../mol-theme/color/chain-id';
 import { OperatorNameColorThemeProvider } from '../../../mol-theme/color/operator-name';
 import { IndexPairBonds } from '../../../mol-model-formats/structure/property/bonds/index-pair';
 import { StructConn } from '../../../mol-model-formats/structure/property/bonds/struct_conn';
+import { StructureRepresentationRegistry } from '../../../mol-repr/structure/registry';
 
 export interface StructureRepresentationPresetProvider<P = any, S extends _Result = _Result> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { }
 export function StructureRepresentationPresetProvider<P, S extends _Result>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; }
@@ -230,7 +231,7 @@ const coarseSurface = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-coarse-surface',
     display: {
         name: 'Coarse Surface', group: BuiltInPresetGroupName,
-        description: 'Shows polymers as coarse Gaussian Surface.'
+        description: 'Shows polymers and lipids as coarse Gaussian Surface.'
     },
     params: () => CommonParams,
     async apply(ref, params, plugin) {
@@ -238,7 +239,8 @@ const coarseSurface = StructureRepresentationPresetProvider({
         if (!structureCell) return {};
 
         const components = {
-            polymer: await presetStaticComponent(plugin, structureCell, 'polymer')
+            polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
+            lipid: await presetStaticComponent(plugin, structureCell, 'lipid'),
         };
 
         const structure = structureCell.obj!.data;
@@ -246,7 +248,7 @@ const coarseSurface = StructureRepresentationPresetProvider({
         const gaussianProps = Object.create(null);
         if (size === Structure.Size.Gigantic) {
             Object.assign(gaussianProps, {
-                traceOnly: true,
+                traceOnly: !structure.isCoarseGrained,
                 radiusOffset: 2,
                 smoothness: 1,
                 visuals: ['structure-gaussian-surface-mesh']
@@ -266,7 +268,8 @@ const coarseSurface = StructureRepresentationPresetProvider({
         const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
 
         const representations = {
-            polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' })
+            polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' }),
+            lipid: builder.buildRepresentation(update, components.lipid, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'lipid' })
         };
 
         await update.commit({ revertOnError: true });
@@ -337,9 +340,15 @@ const atomicDetail = StructureRepresentationPresetProvider({
         const m = structure.models[0];
         const bondsGiven = !!IndexPairBonds.Provider.get(m) || StructConn.isExhaustive(m);
 
-        const atomicType = lowResidueElementRatio && !bondsGiven
-            ? 'spacefill' : highElementCount
-                ? 'line' : 'ball-and-stick';
+        let atomicType: StructureRepresentationRegistry.BuiltIn = 'ball-and-stick';
+        if (structure.isCoarseGrained) {
+            // TODO make configurable?
+            atomicType = structure.elementCount > 1_000_000 ? 'point' : 'spacefill';
+        } else if (lowResidueElementRatio && !bondsGiven) {
+            atomicType = 'spacefill';
+        } else if (highElementCount) {
+            atomicType = 'line';
+        }
         const showCarbohydrateSymbol = params.showCarbohydrateSymbol && !highElementCount && !lowResidueElementRatio;
 
         if (showCarbohydrateSymbol) {

+ 3 - 0
src/mol-plugin-state/manager/structure/focus.ts

@@ -164,7 +164,10 @@ export class StructureFocusManager extends StatefulPluginComponent<StructureFocu
 
         plugin.state.data.events.object.updated.subscribe(({ oldData, obj, action }) => {
             if (!PluginStateObject.Molecule.Structure.is(obj)) return;
+            // structure NOT changed, keep everything as is; fixes #123
+            if (oldData === obj.data) return;
 
+            // structure changed (e.g. coordinates), try to remap and re-focus
             if (action === 'in-place') {
                 const current = this.state.current;
                 const structure = obj.data as Structure;

+ 2 - 2
src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -107,7 +107,7 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber
                 .apply(StateTransforms.Representation.StructureRepresentation3D, this.getReprParams(this.params.surroundingsParams), { tags: StructureFocusRepresentationTags.SurrRepr }).ref;
         }
 
-        if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr]) {
+        if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr] && cell.obj && InteractionsRepresentationProvider.isApplicable(cell.obj?.data)) {
             refs[StructureFocusRepresentationTags.SurrNciRepr] = builder
                 .to(refs[StructureFocusRepresentationTags.SurrSel]!)
                 .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.nciParams, { tags: StructureFocusRepresentationTags.SurrNciRepr }).ref;

+ 5 - 5
src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts

@@ -64,8 +64,8 @@ export namespace VolumeStreaming {
         };
     }
 
-    export type EntryParamDefinition = typeof createEntryParams extends (...args: any[]) => (infer T) ? T : never
-    export type EntryParams = EntryParamDefinition extends PD.Params ? PD.Values<EntryParamDefinition> : {}
+    export type EntryParamDefinition = ReturnType<typeof createEntryParams>
+    export type EntryParams = PD.Values<EntryParamDefinition>
 
     export function createEntryParams(options: { entryData?: VolumeServerInfo.EntryData, defaultView?: ViewTypes, structure?: Structure, channelParams?: DefaultChannelParams }) {
         const { entryData, defaultView, structure, channelParams = { } } = options;
@@ -86,7 +86,7 @@ export namespace VolumeStreaming {
                     bottomLeft: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }),
                     topRight: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }),
                 }, { description: 'Box around focused element.', isFlat: true }),
-                'cell': PD.Group({}),
+                'cell': PD.Group<{}>({}),
                 // Show selection-box if available and cell otherwise.
                 'auto': PD.Group({
                     radius: PD.Numeric(5, { min: 0, max: 50, step: 0.5 }, { description: 'Radius in \u212B within which the volume is shown.' }),
@@ -115,8 +115,8 @@ export namespace VolumeStreaming {
 
     export type ViewTypes = 'off' | 'box' | 'selection-box' | 'cell' | 'auto'
 
-    export type ParamDefinition = typeof createParams extends (...args: any[]) => (infer T) ? T : never
-    export type Params = ParamDefinition extends PD.Params ? PD.Values<ParamDefinition> : {}
+    export type ParamDefinition = ReturnType<typeof createParams>
+    export type Params = PD.Values<ParamDefinition>
 
     type ChannelsInfo = { [name in ChannelType]?: { isoValue: Volume.IsoValue, color: Color, wireframe: boolean, opacity: number } }
     type ChannelsData = { [name in 'EM' | '2FO-FC' | 'FO-FC']?: Volume }

+ 5 - 1
src/mol-repr/structure/representation/spacefill.ts

@@ -21,7 +21,11 @@ export const SpacefillParams = {
 };
 export type SpacefillParams = typeof SpacefillParams
 export function getSpacefillParams(ctx: ThemeRegistryContext, structure: Structure) {
-    return PD.clone(SpacefillParams);
+    const params = PD.clone(SpacefillParams);
+    if (structure.isCoarseGrained) {
+        params.sizeFactor.defaultValue = 2;
+    }
+    return params;
 }
 
 export type SpacefillRepresentation = StructureRepresentation<SpacefillParams>

+ 1 - 2
src/mol-repr/structure/visual/element-point.ts

@@ -18,8 +18,7 @@ import { Sphere3D } from '../../../mol-math/geometry';
 
 export const ElementPointParams = {
     ...UnitsPointsParams,
-    // sizeFactor: PD.Numeric(1.0, { min: 0, max: 10, step: 0.01 }),
-    pointSizeAttenuation: PD.Boolean(false),
+    pointSizeAttenuation: PD.Boolean(true),
     ignoreHydrogens: PD.Boolean(false),
     traceOnly: PD.Boolean(false),
 };