From 27963b5aedd801195aabafc5f7924fe8092e17f0 Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Tue, 13 Aug 2019 14:54:38 -0700
Subject: [PATCH] added option to ignore hydrogens in structure representations

---
 src/mol-math/geometry/common.ts               |  1 +
 src/mol-math/geometry/gaussian-density/cpu.ts |  4 +-
 src/mol-math/geometry/gaussian-density/gpu.ts |  4 +-
 src/mol-math/geometry/molecular-surface.ts    |  6 +-
 .../visual/carbohydrate-link-cylinder.ts      |  3 +-
 .../carbohydrate-terminal-link-cylinder.ts    |  3 +-
 .../visual/cross-link-restraint-cylinder.ts   |  3 +-
 .../structure/visual/element-point.ts         |  1 +
 .../structure/visual/element-sphere.ts        |  8 ++-
 .../visual/gaussian-density-volume.ts         |  4 +-
 .../structure/visual/gaussian-surface-mesh.ts |  6 +-
 .../visual/gaussian-surface-wireframe.ts      |  5 +-
 .../visual/inter-unit-link-cylinder.ts        | 14 ++++-
 .../visual/intra-unit-link-cylinder.ts        | 12 +++-
 .../visual/molecular-surface-mesh.ts          |  8 ++-
 src/mol-repr/structure/visual/util/common.ts  | 62 ++++++++++++++-----
 src/mol-repr/structure/visual/util/element.ts | 12 +++-
 .../structure/visual/util/gaussian.ts         | 12 ++--
 src/mol-repr/structure/visual/util/link.ts    |  5 +-
 .../visual/util/molecular-surface.ts          | 13 ++--
 20 files changed, 133 insertions(+), 53 deletions(-)

diff --git a/src/mol-math/geometry/common.ts b/src/mol-math/geometry/common.ts
index 60fbd3c44..184034082 100644
--- a/src/mol-math/geometry/common.ts
+++ b/src/mol-math/geometry/common.ts
@@ -14,6 +14,7 @@ export interface PositionData {
     x: ArrayLike<number>,
     y: ArrayLike<number>,
     z: ArrayLike<number>,
+    id: ArrayLike<number>,
     /** subset of indices into the x/y/z/radius arrays */
     indices: OrderedSet,
     /** optional element radius */
diff --git a/src/mol-math/geometry/gaussian-density/cpu.ts b/src/mol-math/geometry/gaussian-density/cpu.ts
index d1a8c2d16..bb74c6d12 100644
--- a/src/mol-math/geometry/gaussian-density/cpu.ts
+++ b/src/mol-math/geometry/gaussian-density/cpu.ts
@@ -15,7 +15,7 @@ export async function GaussianDensityCPU(ctx: RuntimeContext, position: Position
     const { resolution, radiusOffset, smoothness } = props
     const scaleFactor = 1 / resolution
 
-    const { indices, x, y, z } = position
+    const { indices, x, y, z, id } = position
     const n = OrderedSet.size(indices)
     const radii = new Float32Array(n)
 
@@ -100,7 +100,7 @@ export async function GaussianDensityCPU(ctx: RuntimeContext, position: Position
                             data[idx] += dens
                             if (dens > densData[idx]) {
                                 densData[idx] = dens
-                                idData[idx] = i
+                                idData[idx] = id[i]
                             }
                         }
                     }
diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts
index 949d5cae7..2128478a6 100644
--- a/src/mol-math/geometry/gaussian-density/gpu.ts
+++ b/src/mol-math/geometry/gaussian-density/gpu.ts
@@ -224,7 +224,7 @@ function prepareGaussianDensityData(position: PositionData, box: Box3D, radius:
     const { resolution, radiusOffset } = props
     const scaleFactor = 1 / resolution
 
-    const { indices, x, y, z } = position
+    const { indices, x, y, z, id } = position
     const n = OrderedSet.size(indices)
 
     const positions = new Float32Array(n * 3)
@@ -242,7 +242,7 @@ function prepareGaussianDensityData(position: PositionData, box: Box3D, radius:
         const r = radius(j) + radiusOffset
         if (maxRadius < r) maxRadius = r
         radii[i] = r
-        groups[i] = i
+        groups[i] = id[i]
     }
 
     const pad = maxRadius * 2 + resolution * 4
diff --git a/src/mol-math/geometry/molecular-surface.ts b/src/mol-math/geometry/molecular-surface.ts
index a133e6704..be6c60127 100644
--- a/src/mol-math/geometry/molecular-surface.ts
+++ b/src/mol-math/geometry/molecular-surface.ts
@@ -169,7 +169,7 @@ export async function calcMolecularSurface(ctx: RuntimeContext, position: Requir
                                 const dd = rad - d
                                 if (dd < data[idx]) {
                                     data[idx] = dd
-                                    idData[idx] = i
+                                    idData[idx] = id[i]
                                 }
                             }
                         }
@@ -282,7 +282,7 @@ export async function calcMolecularSurface(ctx: RuntimeContext, position: Requir
                                 // Is this grid point closer to a or b?
                                 // Take dot product of atob and gridpoint->p (dx, dy, dz)
                                 const dp = dx * atob[0] + dy * atob[1] + dz * atob[2]
-                                idData[idx] = OrderedSet.indexOf(position.indices, dp < 0.0 ? b : a)
+                                idData[idx] = id[OrderedSet.indexOf(position.indices, dp < 0.0 ? b : a)]
                             }
                         }
                     }
@@ -323,7 +323,7 @@ export async function calcMolecularSurface(ctx: RuntimeContext, position: Requir
     const lookup3d = GridLookup3D(position, cellSize)
     const neighbours = lookup3d.result
     const box = lookup3d.boundary.box
-    const { indices, x: px, y: py, z: pz, radius } = position
+    const { indices, x: px, y: py, z: pz, id, radius } = position
     const n = OrderedSet.size(indices)
 
     const pad = maxRadius * 2 + resolution
diff --git a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
index 094aef3ee..382adbf8f 100644
--- a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
@@ -42,7 +42,8 @@ function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: Struc
             location.unit = elements[l.carbohydrateIndexA].unit
             location.element = elements[l.carbohydrateIndexA].anomericCarbon
             return theme.size.size(location) * linkSizeFactor
-        }
+        },
+        ignore: (edgeIndex: number) => false
     }
 
     return createLinkCylinderMesh(ctx, builderProps, props, mesh)
diff --git a/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
index 460343df6..b0c92d1f7 100644
--- a/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
@@ -52,7 +52,8 @@ function createCarbohydrateTerminalLinkCylinderMesh(ctx: VisualContext, structur
                 location.element = l.elementUnit.elements[l.elementIndex]
             }
             return theme.size.size(location) * linkSizeFactor
-        }
+        },
+        ignore: (edgeIndex: number) => false
     }
 
     return createLinkCylinderMesh(ctx, builderProps, props, mesh)
diff --git a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
index 1971a36e4..b215e9e03 100644
--- a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
+++ b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
@@ -44,7 +44,8 @@ function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Str
             location.unit = b.unitA
             location.element = b.unitA.elements[b.indexA]
             return theme.size.size(location) * sizeFactor
-        }
+        },
+        ignore: () => false
     }
 
     return createLinkCylinderMesh(ctx, builderProps, props, mesh)
diff --git a/src/mol-repr/structure/visual/element-point.ts b/src/mol-repr/structure/visual/element-point.ts
index 1ee303c06..f234613c3 100644
--- a/src/mol-repr/structure/visual/element-point.ts
+++ b/src/mol-repr/structure/visual/element-point.ts
@@ -19,6 +19,7 @@ export const ElementPointParams = {
     ...UnitsPointsParams,
     // sizeFactor: PD.Numeric(1.0, { min: 0, max: 10, step: 0.01 }),
     pointSizeAttenuation: PD.Boolean(false),
+    showHydrogens: PD.Boolean(true),
 }
 export type ElementPointParams = typeof ElementPointParams
 
diff --git a/src/mol-repr/structure/visual/element-sphere.ts b/src/mol-repr/structure/visual/element-sphere.ts
index cffa6ff95..76715a640 100644
--- a/src/mol-repr/structure/visual/element-sphere.ts
+++ b/src/mol-repr/structure/visual/element-sphere.ts
@@ -16,6 +16,7 @@ export const ElementSphereParams = {
     ...UnitsSpheresParams,
     sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
     detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
+    ignoreHydrogens: PD.Boolean(false),
 }
 export type ElementSphereParams = typeof ElementSphereParams
 
@@ -31,7 +32,9 @@ export function ElementSphereImpostorVisual(materialId: number): UnitsVisual<Ele
         getLoci: getElementLoci,
         eachLocation: eachElement,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementSphereParams>, currentProps: PD.Values<ElementSphereParams>) => {
-
+            state.createGeometry = (
+                newProps.ignoreHydrogens !== currentProps.ignoreHydrogens
+            )
         }
     }, materialId)
 }
@@ -46,7 +49,8 @@ export function ElementSphereMeshVisual(materialId: number): UnitsVisual<Element
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementSphereParams>, currentProps: PD.Values<ElementSphereParams>) => {
             state.createGeometry = (
                 newProps.sizeFactor !== currentProps.sizeFactor ||
-                newProps.detail !== currentProps.detail
+                newProps.detail !== currentProps.detail ||
+                newProps.ignoreHydrogens !== currentProps.ignoreHydrogens
             )
         }
     }, materialId)
diff --git a/src/mol-repr/structure/visual/gaussian-density-volume.ts b/src/mol-repr/structure/visual/gaussian-density-volume.ts
index 7c49603af..dd12a7b82 100644
--- a/src/mol-repr/structure/visual/gaussian-density-volume.ts
+++ b/src/mol-repr/structure/visual/gaussian-density-volume.ts
@@ -30,7 +30,8 @@ async function createGaussianDensityVolume(ctx: VisualContext, structure: Struct
 
 export const GaussianDensityVolumeParams = {
     ...ComplexDirectVolumeParams,
-    ...GaussianDensityTextureParams
+    ...GaussianDensityTextureParams,
+    ignoreHydrogens: PD.Boolean(false),
 }
 export type GaussianDensityVolumeParams = typeof GaussianDensityVolumeParams
 
@@ -48,6 +49,7 @@ export function GaussianDensityVolumeVisual(materialId: number): ComplexVisual<G
                 state.createGeometry = true
                 newProps.isoValueNorm = Math.exp(-newProps.smoothness)
             }
+            if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true
         }
     }, materialId)
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
index f937cedf0..d77e36c94 100644
--- a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
+++ b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
@@ -6,12 +6,11 @@
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { UnitsMeshParams, UnitsTextureMeshParams, UnitsVisual, UnitsMeshVisual, UnitsTextureMeshVisual } from '../units-visual';
-import { GaussianDensityParams, computeUnitGaussianDensity, GaussianDensityTextureProps, computeUnitGaussianDensityTexture2d } from './util/gaussian';
+import { GaussianDensityParams, computeUnitGaussianDensity, GaussianDensityTextureProps, computeUnitGaussianDensityTexture2d, GaussianDensityProps } from './util/gaussian';
 import { WebGLContext } from '../../../mol-gl/webgl/context';
 import { VisualContext } from '../../visual';
 import { Unit, Structure } from '../../../mol-model/structure';
 import { Theme } from '../../../mol-theme/theme';
-import { GaussianDensityProps } from '../../../mol-math/geometry/gaussian-density';
 import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
 import { computeMarchingCubesMesh } from '../../../mol-geo/util/marching-cubes/algorithm';
 import { StructureElementIterator, getElementLoci, eachElement } from './util/element';
@@ -26,6 +25,7 @@ export const GaussianSurfaceMeshParams = {
     ...UnitsMeshParams,
     ...UnitsTextureMeshParams,
     ...GaussianDensityParams,
+    ignoreHydrogens: PD.Boolean(false),
 }
 export type GaussianSurfaceMeshParams = typeof GaussianSurfaceMeshParams
 
@@ -64,6 +64,7 @@ export function GaussianSurfaceMeshVisual(materialId: number): UnitsVisual<Gauss
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true
             if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true
+            if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true
         }
     }, materialId)
 }
@@ -108,6 +109,7 @@ export function GaussianSurfaceTextureMeshVisual(materialId: number): UnitsVisua
             if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true
+            if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true
         }
     }, materialId)
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
index 241f86b8b..bc5b4910f 100644
--- a/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
+++ b/src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
@@ -8,9 +8,8 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { VisualContext } from '../../visual';
 import { Unit, Structure } from '../../../mol-model/structure';
 import { Theme } from '../../../mol-theme/theme';
-import { GaussianDensityProps } from '../../../mol-math/geometry/gaussian-density';
 import { Lines } from '../../../mol-geo/geometry/lines/lines';
-import { computeUnitGaussianDensity, GaussianDensityParams } from './util/gaussian';
+import { computeUnitGaussianDensity, GaussianDensityParams, GaussianDensityProps } from './util/gaussian';
 import { computeMarchingCubesLines } from '../../../mol-geo/util/marching-cubes/algorithm';
 import { UnitsLinesParams, UnitsVisual, UnitsLinesVisual } from '../units-visual';
 import { StructureElementIterator, getElementLoci, eachElement } from './util/element';
@@ -36,6 +35,7 @@ export const GaussianWireframeParams = {
     ...UnitsLinesParams,
     ...GaussianDensityParams,
     lineSizeAttenuation: PD.Boolean(false),
+    ignoreHydrogens: PD.Boolean(false),
 }
 export type GaussianWireframeParams = typeof GaussianWireframeParams
 
@@ -51,6 +51,7 @@ export function GaussianWireframeVisual(materialId: number): UnitsVisual<Gaussia
             if (newProps.radiusOffset !== currentProps.radiusOffset) state.createGeometry = true
             if (newProps.smoothness !== currentProps.smoothness) state.createGeometry = true
             if (newProps.useGpu !== currentProps.useGpu) state.createGeometry = true
+            if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true
         }
     }, materialId)
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
index 775534ce9..615f29cb4 100644
--- a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
@@ -17,11 +17,12 @@ import { VisualUpdateState } from '../../util';
 import { PickingId } from '../../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../../mol-model/loci';
 import { Interval, OrderedSet } from '../../../mol-data/int';
+import { isHydrogen } from './util/common';
 
 function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitLinkParams>, mesh?: Mesh) {
     const links = structure.links
     const { bondCount, bonds } = links
-    const { sizeFactor, sizeAspectRatio } = props
+    const { sizeFactor, sizeAspectRatio, ignoreHydrogens } = props
 
     if (!bondCount) return Mesh.createEmpty(mesh)
 
@@ -43,7 +44,12 @@ function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: Structur
             location.unit = b.unitA
             location.element = b.unitA.elements[b.indexA]
             return theme.size.size(location) * sizeFactor * sizeAspectRatio
-        }
+        },
+        ignore: ignoreHydrogens ? (edgeIndex: number) => {
+            const b = bonds[edgeIndex]
+            const uA = b.unitA, uB = b.unitB
+            return isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB])
+        } : () => false
     }
 
     return createLinkCylinderMesh(ctx, builderProps, props, mesh)
@@ -54,6 +60,7 @@ export const InterUnitLinkParams = {
     ...LinkCylinderParams,
     sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
     sizeAspectRatio: PD.Numeric(2/3, { min: 0, max: 3, step: 0.01 }),
+    ignoreHydrogens: PD.Boolean(false),
 }
 export type InterUnitLinkParams = typeof InterUnitLinkParams
 
@@ -70,7 +77,8 @@ export function InterUnitLinkVisual(materialId: number): ComplexVisual<InterUnit
                 newProps.sizeAspectRatio !== currentProps.sizeAspectRatio ||
                 newProps.radialSegments !== currentProps.radialSegments ||
                 newProps.linkScale !== currentProps.linkScale ||
-                newProps.linkSpacing !== currentProps.linkSpacing
+                newProps.linkSpacing !== currentProps.linkSpacing ||
+                newProps.ignoreHydrogens !== currentProps.ignoreHydrogens
             )
         }
     }, materialId)
diff --git a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
index a881695b1..4ead21c95 100644
--- a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
@@ -18,6 +18,7 @@ import { VisualUpdateState } from '../../util';
 import { PickingId } from '../../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci } from '../../../mol-model/loci';
 import { Interval, OrderedSet } from '../../../mol-data/int';
+import { isHydrogen } from './util/common';
 
 function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitLinkParams>, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
@@ -28,7 +29,7 @@ function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structu
     const links = unit.links
     const { edgeCount, a, b, edgeProps, offset } = links
     const { order: _order, flags: _flags } = edgeProps
-    const { sizeFactor, sizeAspectRatio } = props
+    const { sizeFactor, sizeAspectRatio, ignoreHydrogens } = props
 
     if (!edgeCount) return Mesh.createEmpty(mesh)
 
@@ -63,7 +64,10 @@ function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, structu
         radius: (edgeIndex: number) => {
             location.element = elements[a[edgeIndex]]
             return theme.size.size(location) * sizeFactor * sizeAspectRatio
-        }
+        },
+        ignore: ignoreHydrogens ? (edgeIndex: number) => {
+            return isHydrogen(unit, elements[a[edgeIndex]]) || isHydrogen(unit, elements[b[edgeIndex]])
+        } : () => false
     }
 
     return createLinkCylinderMesh(ctx, builderProps, props, mesh)
@@ -74,6 +78,7 @@ export const IntraUnitLinkParams = {
     ...LinkCylinderParams,
     sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
     sizeAspectRatio: PD.Numeric(2/3, { min: 0, max: 3, step: 0.01 }),
+    ignoreHydrogens: PD.Boolean(false),
 }
 export type IntraUnitLinkParams = typeof IntraUnitLinkParams
 
@@ -90,7 +95,8 @@ export function IntraUnitLinkVisual(materialId: number): UnitsVisual<IntraUnitLi
                 newProps.sizeAspectRatio !== currentProps.sizeAspectRatio ||
                 newProps.radialSegments !== currentProps.radialSegments ||
                 newProps.linkScale !== currentProps.linkScale ||
-                newProps.linkSpacing !== currentProps.linkSpacing
+                newProps.linkSpacing !== currentProps.linkSpacing ||
+                newProps.ignoreHydrogens !== currentProps.ignoreHydrogens
             )
         }
     }, materialId)
diff --git a/src/mol-repr/structure/visual/molecular-surface-mesh.ts b/src/mol-repr/structure/visual/molecular-surface-mesh.ts
index 2aa8c863e..cba0730ac 100644
--- a/src/mol-repr/structure/visual/molecular-surface-mesh.ts
+++ b/src/mol-repr/structure/visual/molecular-surface-mesh.ts
@@ -6,12 +6,12 @@
 
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { UnitsMeshParams, UnitsTextureMeshParams, UnitsVisual, UnitsMeshVisual } from '../units-visual';
-import { MolecularSurfaceCalculationParams, MolecularSurfaceCalculationProps } from '../../../mol-math/geometry/molecular-surface';
+import { MolecularSurfaceCalculationParams } from '../../../mol-math/geometry/molecular-surface';
 import { VisualContext } from '../../visual';
 import { Unit, Structure } from '../../../mol-model/structure';
 import { Theme } from '../../../mol-theme/theme';
 import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
-import { computeUnitMolecularSurface } from './util/molecular-surface';
+import { computeUnitMolecularSurface, MolecularSurfaceProps } from './util/molecular-surface';
 import { computeMarchingCubesMesh } from '../../../mol-geo/util/marching-cubes/algorithm';
 import { StructureElementIterator, getElementLoci, eachElement } from './util/element';
 import { VisualUpdateState } from '../../util';
@@ -20,12 +20,13 @@ export const MolecularSurfaceMeshParams = {
     ...UnitsMeshParams,
     ...UnitsTextureMeshParams,
     ...MolecularSurfaceCalculationParams,
+    ignoreHydrogens: PD.Boolean(false),
 }
 export type MolecularSurfaceMeshParams = typeof MolecularSurfaceMeshParams
 
 //
 
-async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: MolecularSurfaceCalculationProps, mesh?: Mesh): Promise<Mesh> {
+async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: MolecularSurfaceProps, mesh?: Mesh): Promise<Mesh> {
 
     const { transform, field, idField } = await computeUnitMolecularSurface(unit, props).runInContext(ctx.runtime)
     const params = {
@@ -52,6 +53,7 @@ export function MolecularSurfaceMeshVisual(materialId: number): UnitsVisual<Mole
             if (newProps.resolution !== currentProps.resolution) state.createGeometry = true
             if (newProps.probeRadius !== currentProps.probeRadius) state.createGeometry = true
             if (newProps.probePositions !== currentProps.probePositions) state.createGeometry = true
+            if (newProps.ignoreHydrogens !== currentProps.ignoreHydrogens) state.createGeometry = true
         }
     }, materialId)
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/util/common.ts b/src/mol-repr/structure/visual/util/common.ts
index de52db7f5..33d909faf 100644
--- a/src/mol-repr/structure/visual/util/common.ts
+++ b/src/mol-repr/structure/visual/util/common.ts
@@ -10,6 +10,8 @@ import { TransformData, createTransform } from '../../../../mol-geo/geometry/tra
 import { OrderedSet, SortedArray } from '../../../../mol-data/int';
 import { EmptyLoci, Loci } from '../../../../mol-model/loci';
 import { PhysicalSizeTheme } from '../../../../mol-theme/size/physical';
+import { AtomicNumbers, AtomNumber } from '../../../../mol-model/structure/model/properties/atomic';
+import { fillSerial } from '../../../../mol-util/array';
 
 /** Return a Loci for the elements of a whole residue the elementIndex belongs to. */
 export function getResidueLoci(structure: Structure, unit: Unit.Atomic, elementIndex: ElementIndex): Loci {
@@ -95,14 +97,34 @@ export function getConformation(unit: Unit) {
     }
 }
 
-export function getUnitConformationAndRadius(unit: Unit) {
+export function getUnitConformationAndRadius(unit: Unit, ignoreHydrogens = false) {
     const conformation = getConformation(unit)
     const { elements } = unit
+
+    let indices: SortedArray<ElementIndex>
+    let id: ArrayLike<number>
+
+    if (ignoreHydrogens) {
+        const _indices = []
+        const _id = []
+        for (let i = 0, il = elements.length; i < il; ++i) {
+            if (isHydrogen(unit, elements[i])) continue
+            _indices.push(elements[i])
+            _id.push(i)
+        }
+        indices = SortedArray.ofSortedArray(_indices)
+        id = _id
+    } else {
+        indices = elements
+        id = fillSerial(new Uint32Array(indices.length))
+    }
+
     const position = {
-        indices: elements,
+        indices,
         x: conformation.x,
         y: conformation.y,
-        z: conformation.z
+        z: conformation.z,
+        id
     }
 
     const l = StructureElement.create(unit)
@@ -115,13 +137,12 @@ export function getUnitConformationAndRadius(unit: Unit) {
     return { position, radius }
 }
 
-export function getStructureConformationAndRadius(structure: Structure) {
-    const n = structure.elementCount
-
-    const xs = new Float32Array(n)
-    const ys = new Float32Array(n)
-    const zs = new Float32Array(n)
-    const rs = new Float32Array(n)
+export function getStructureConformationAndRadius(structure: Structure, ignoreHydrogens = false) {
+    const xs: number[] = []
+    const ys: number[] = []
+    const zs: number[] = []
+    const rs: number[] = []
+    const id: number[] = []
 
     const l = StructureElement.create()
     const sizeTheme = PhysicalSizeTheme({}, {})
@@ -134,17 +155,28 @@ export function getStructureConformationAndRadius(structure: Structure) {
         l.unit = unit
         for (let j = 0, jl = elements.length; j < jl; ++j) {
             const eI = elements[j]
-            xs[m + j] = x(eI)
-            ys[m + j] = y(eI)
-            zs[m + j] = z(eI)
+            if (ignoreHydrogens && isHydrogen(unit, eI)) continue
+
+            const mj = m + j
+            xs[mj] = x(eI)
+            ys[mj] = y(eI)
+            zs[mj] = z(eI)
             l.element = eI
-            rs[m + j] = sizeTheme.size(l)
+            rs[mj] = sizeTheme.size(l)
+            id[mj] = mj
         }
         m += elements.length
     }
 
-    const position = { indices: OrderedSet.ofRange(0, n), x: xs, y: ys, z: zs }
+    const position = { indices: OrderedSet.ofRange(0, m), x: xs, y: ys, z: zs, id }
     const radius = (index: number) => rs[index]
 
     return { position, radius }
+}
+
+const _H = AtomicNumbers['H']
+export function isHydrogen(unit: Unit, element: ElementIndex) {
+    if (Unit.isCoarse(unit)) return false
+    if (AtomNumber(unit.model.atomicHierarchy.atoms.type_symbol.value(element)) === _H) return true
+    return false
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/util/element.ts b/src/mol-repr/structure/visual/util/element.ts
index d70dc06a3..25f536c9a 100644
--- a/src/mol-repr/structure/visual/util/element.ts
+++ b/src/mol-repr/structure/visual/util/element.ts
@@ -19,10 +19,12 @@ import { Theme } from '../../../../mol-theme/theme';
 import { StructureGroup } from '../../../../mol-repr/structure/units-visual';
 import { Spheres } from '../../../../mol-geo/geometry/spheres/spheres';
 import { SpheresBuilder } from '../../../../mol-geo/geometry/spheres/spheres-builder';
+import { isHydrogen } from './common';
 
 export interface ElementSphereMeshProps {
     detail: number,
-    sizeFactor: number
+    sizeFactor: number,
+    ignoreHydrogens: boolean,
 }
 
 export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereMeshProps, mesh?: Mesh): Mesh {
@@ -39,6 +41,8 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
     l.unit = unit
 
     for (let i = 0; i < elementCount; i++) {
+        if (props.ignoreHydrogens && isHydrogen(unit, elements[i])) continue
+
         l.element = elements[i]
         pos(elements[i], v)
 
@@ -49,7 +53,9 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
     return MeshBuilder.getMesh(builderState)
 }
 
-export interface ElementSphereImpostorProps { }
+export interface ElementSphereImpostorProps {
+    ignoreHydrogens: boolean,
+}
 
 export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereImpostorProps, spheres?: Spheres): Spheres {
 
@@ -61,6 +67,8 @@ export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, stru
     const pos = unit.conformation.invariantPosition
 
     for (let i = 0; i < elementCount; i++) {
+        if (props.ignoreHydrogens && isHydrogen(unit, elements[i])) continue
+
         pos(elements[i], v)
         builder.add(v[0], v[1], v[2], i)
     }
diff --git a/src/mol-repr/structure/visual/util/gaussian.ts b/src/mol-repr/structure/visual/util/gaussian.ts
index 401e68a60..fb8509759 100644
--- a/src/mol-repr/structure/visual/util/gaussian.ts
+++ b/src/mol-repr/structure/visual/util/gaussian.ts
@@ -18,6 +18,7 @@ export const GaussianDensityParams = {
     radiusOffset: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }),
     smoothness: PD.Numeric(1.5, { min: 0.5, max: 2.5, step: 0.1 }),
     useGpu: PD.Boolean(false),
+    ignoreHydrogens: PD.Boolean(false),
 }
 export const DefaultGaussianDensityProps = PD.getDefaultValues(GaussianDensityParams)
 export type GaussianDensityProps = typeof DefaultGaussianDensityProps
@@ -26,6 +27,7 @@ export const GaussianDensityTextureParams = {
     resolution: PD.Numeric(1, { min: 0.1, max: 20, step: 0.1 }),
     radiusOffset: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }),
     smoothness: PD.Numeric(1.5, { min: 0.5, max: 2.5, step: 0.1 }),
+    ignoreHydrogens: PD.Boolean(false),
 }
 export const DefaultGaussianDensityTextureProps = PD.getDefaultValues(GaussianDensityTextureParams)
 export type GaussianDensityTextureProps = typeof DefaultGaussianDensityTextureProps
@@ -33,21 +35,21 @@ export type GaussianDensityTextureProps = typeof DefaultGaussianDensityTexturePr
 //
 
 export function computeUnitGaussianDensity(unit: Unit, props: GaussianDensityProps, webgl?: WebGLContext) {
-    const { position, radius } = getUnitConformationAndRadius(unit)
+    const { position, radius } = getUnitConformationAndRadius(unit, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return await GaussianDensity(ctx, position, unit.lookup3d.boundary.box, radius, props, webgl);
     });
 }
 
 export function computeUnitGaussianDensityTexture(unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
-    const { position, radius } = getUnitConformationAndRadius(unit)
+    const { position, radius } = getUnitConformationAndRadius(unit, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return GaussianDensityTexture(webgl, position, unit.lookup3d.boundary.box, radius, props, texture);
     });
 }
 
 export function computeUnitGaussianDensityTexture2d(unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
-    const { position, radius } = getUnitConformationAndRadius(unit)
+    const { position, radius } = getUnitConformationAndRadius(unit, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return GaussianDensityTexture2d(webgl, position, unit.lookup3d.boundary.box, radius, props, texture);
     });
@@ -56,14 +58,14 @@ export function computeUnitGaussianDensityTexture2d(unit: Unit, props: GaussianD
 //
 
 export function computeStructureGaussianDensity(structure: Structure, props: GaussianDensityProps, webgl?: WebGLContext) {
-    const { position, radius } = getStructureConformationAndRadius(structure)
+    const { position, radius } = getStructureConformationAndRadius(structure, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return await GaussianDensity(ctx, position, structure.lookup3d.boundary.box, radius, props, webgl);
     });
 }
 
 export function computeStructureGaussianDensityTexture(structure: Structure, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
-    const { position, radius } = getStructureConformationAndRadius(structure)
+    const { position, radius } = getStructureConformationAndRadius(structure, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return GaussianDensityTexture(webgl, position, structure.lookup3d.boundary.box, radius, props, texture);
     });
diff --git a/src/mol-repr/structure/visual/util/link.ts b/src/mol-repr/structure/visual/util/link.ts
index 0952cd022..69079b62e 100644
--- a/src/mol-repr/structure/visual/util/link.ts
+++ b/src/mol-repr/structure/visual/util/link.ts
@@ -59,6 +59,7 @@ export interface LinkCylinderMeshBuilderProps {
     order(edgeIndex: number): number
     flags(edgeIndex: number): LinkType
     radius(edgeIndex: number): number
+    ignore(edgeIndex: number): boolean
 }
 
 /**
@@ -66,7 +67,7 @@ export interface LinkCylinderMeshBuilderProps {
  * the half closer to the first vertex, i.e. vertex a.
  */
 export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkCylinderMeshBuilderProps, props: LinkCylinderProps, mesh?: Mesh) {
-    const { linkCount, referencePosition, position, order, flags, radius } = linkBuilder
+    const { linkCount, referencePosition, position, order, flags, radius, ignore } = linkBuilder
 
     if (!linkCount) return Mesh.createEmpty(mesh)
 
@@ -87,6 +88,8 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkCyli
     }
 
     for (let edgeIndex = 0, _eI = linkCount; edgeIndex < _eI; ++edgeIndex) {
+        if (ignore(edgeIndex)) continue
+
         position(va, vb, edgeIndex)
 
         const linkRadius = radius(edgeIndex)
diff --git a/src/mol-repr/structure/visual/util/molecular-surface.ts b/src/mol-repr/structure/visual/util/molecular-surface.ts
index 4444ec061..5d3fd7491 100644
--- a/src/mol-repr/structure/visual/util/molecular-surface.ts
+++ b/src/mol-repr/structure/visual/util/molecular-surface.ts
@@ -11,8 +11,13 @@ import { PositionData, DensityData } from '../../../../mol-math/geometry';
 import { MolecularSurfaceCalculationProps, calcMolecularSurface } from '../../../../mol-math/geometry/molecular-surface';
 import { OrderedSet } from '../../../../mol-data/int';
 
-function getPositionDataAndMaxRadius(unit: Unit, props: MolecularSurfaceCalculationProps) {
-    const { position, radius } = getUnitConformationAndRadius(unit)
+export type MolecularSurfaceProps = MolecularSurfaceCalculationProps & {
+    ignoreHydrogens: boolean
+}
+
+function getPositionDataAndMaxRadius(unit: Unit, props: MolecularSurfaceProps) {
+    const { probeRadius, ignoreHydrogens } = props
+    const { position, radius } = getUnitConformationAndRadius(unit, ignoreHydrogens)
     const { indices } = position
     const n = OrderedSet.size(indices)
     const radii = new Float32Array(OrderedSet.end(indices))
@@ -22,13 +27,13 @@ function getPositionDataAndMaxRadius(unit: Unit, props: MolecularSurfaceCalculat
         const j = OrderedSet.getAt(indices, i)
         const r = radius(j)
         if (maxRadius < r) maxRadius = r
-        radii[j] = r + props.probeRadius
+        radii[j] = r + probeRadius
     }
 
     return { position: { ...position, radius: radii }, maxRadius }
 }
 
-export function computeUnitMolecularSurface(unit: Unit, props: MolecularSurfaceCalculationProps) {
+export function computeUnitMolecularSurface(unit: Unit, props: MolecularSurfaceProps) {
     const { position, maxRadius } = getPositionDataAndMaxRadius(unit, props)
     return Task.create('Molecular Surface', async ctx => {
         return await MolecularSurface(ctx, position, maxRadius, props);
-- 
GitLab