Browse Source

Merge branch 'master' of https://github.com/molstar/molstar into forkdev

autin 5 years ago
parent
commit
0b9371527e

File diff suppressed because it is too large
+ 444 - 350
package-lock.json


+ 18 - 18
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "0.7.0-dev.6",
+  "version": "0.7.0-dev.10",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -82,52 +82,52 @@
   ],
   "license": "MIT",
   "devDependencies": {
-    "@graphql-codegen/add": "^1.13.2",
-    "@graphql-codegen/cli": "^1.13.2",
-    "@graphql-codegen/time": "^1.13.2",
-    "@graphql-codegen/typescript": "^1.13.2",
-    "@graphql-codegen/typescript-graphql-files-modules": "^1.13.2",
-    "@graphql-codegen/typescript-graphql-request": "^1.13.2",
-    "@graphql-codegen/typescript-operations": "^1.13.2",
+    "@graphql-codegen/add": "^1.13.3",
+    "@graphql-codegen/cli": "^1.13.3",
+    "@graphql-codegen/time": "^1.13.3",
+    "@graphql-codegen/typescript": "^1.13.3",
+    "@graphql-codegen/typescript-graphql-files-modules": "^1.13.3",
+    "@graphql-codegen/typescript-graphql-request": "^1.13.3",
+    "@graphql-codegen/typescript-operations": "^1.13.3",
     "@types/cors": "^2.8.6",
-    "@typescript-eslint/eslint-plugin": "^2.28.0",
-    "@typescript-eslint/parser": "^2.28.0",
+    "@typescript-eslint/eslint-plugin": "^2.29.0",
+    "@typescript-eslint/parser": "^2.29.0",
     "benchmark": "^2.1.4",
     "circular-dependency-plugin": "^5.2.0",
     "concurrently": "^5.1.0",
     "cpx2": "^2.0.0",
-    "css-loader": "^3.5.2",
+    "css-loader": "^3.5.3",
     "eslint": "^6.8.0",
     "extra-watch-webpack-plugin": "^1.0.3",
     "file-loader": "^6.0.0",
     "fs-extra": "^9.0.0",
     "graphql": "^15.0.0",
     "http-server": "^0.12.1",
-    "jest": "^25.3.0",
+    "jest": "^25.4.0",
     "jest-raw-loader": "^1.0.1",
     "mini-css-extract-plugin": "^0.9.0",
-    "node-sass": "^4.13.1",
+    "node-sass": "^4.14.0",
     "raw-loader": "^4.0.1",
     "resolve-url-loader": "^3.1.1",
     "sass-loader": "^8.0.2",
     "simple-git": "^1.132.0",
-    "style-loader": "^1.1.4",
+    "style-loader": "^1.2.0",
     "ts-jest": "^25.4.0",
     "typescript": "^3.8.3",
-    "webpack": "^4.42.1",
+    "webpack": "^4.43.0",
     "webpack-cli": "^3.3.11",
     "webpack-version-file-plugin": "^0.4.0"
   },
   "dependencies": {
-    "@material-ui/core": "^4.9.10",
+    "@material-ui/core": "^4.9.11",
     "@material-ui/icons": "^4.9.1",
     "@types/argparse": "^1.0.38",
     "@types/benchmark": "^1.0.31",
     "@types/compression": "1.7.0",
     "@types/express": "^4.17.6",
     "@types/jest": "^25.2.1",
-    "@types/node": "^13.13.0",
-    "@types/node-fetch": "^2.5.6",
+    "@types/node": "^13.13.2",
+    "@types/node-fetch": "^2.5.7",
     "@types/react": "^16.9.34",
     "@types/react-dom": "^16.9.6",
     "@types/swagger-ui-dist": "3.0.5",

+ 3 - 0
src/mol-gl/shader/chunks/apply-fog.glsl.ts

@@ -1,8 +1,11 @@
 export default `
 float depth = length(vViewPosition);
 float fogFactor = smoothstep(uFogNear, uFogFar, depth);
+float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
 if (uTransparentBackground == 0) {
     gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
+    if (gl_FragColor.a < 1.0)
+        gl_FragColor.a = fogAlpha;
 } else {
     float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
     gl_FragColor.a = fogAlpha;

+ 15 - 13
src/mol-gl/shader/image.frag.ts

@@ -35,20 +35,20 @@ varying float vInstance;
             const float C = 0.333;
         #endif
 
-        float cubicFilter( float x ){
+        float cubicFilter(float x){
             float f = x;
-            if( f < 0.0 ){
+            if (f < 0.0) {
                 f = -f;
             }
-            if( f < 1.0 ){
-                return ( ( 12.0 - 9.0 * B - 6.0 * C ) * ( f * f * f ) +
-                    ( -18.0 + 12.0 * B + 6.0 *C ) * ( f * f ) +
-                    ( 6.0 - 2.0 * B ) ) / 6.0;
-            }else if( f >= 1.0 && f < 2.0 ){
-                return ( ( -B - 6.0 * C ) * ( f * f * f )
-                    + ( 6.0 * B + 30.0 * C ) * ( f *f ) +
-                    ( - ( 12.0 * B ) - 48.0 * C  ) * f +
-                    8.0 * B + 24.0 * C ) / 6.0;
+            if (f < 1.0) {
+                return ((12.0 - 9.0 * B - 6.0 * C) * (f * f * f) +
+                    (-18.0 + 12.0 * B + 6.0 * C) * (f * f) +
+                    (6.0 - 2.0 * B)) / 6.0;
+            }else if (f >= 1.0 && f < 2.0){
+                return ((-B - 6.0 * C) * ( f * f * f)
+                    + (6.0 * B + 30.0 * C) * (f * f) +
+                    (-(12.0 * B) - 48.0 * C) * f +
+                    8.0 * B + 24.0 * C) / 6.0;
             }else{
                 return 0.0;
             }
@@ -92,6 +92,8 @@ void main() {
     #else
         vec4 imageData = texture2D(tImageTex, vUv);
     #endif
+    imageData.a = clamp(imageData.a, 0.0, 1.0);
+    if (imageData.a > 0.9) imageData.a = 1.0;
 
     #if defined(dRenderVariant_pick)
         if (imageData.a < 0.3)
@@ -110,7 +112,7 @@ void main() {
             gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
         }
     #elif defined(dRenderVariant_depth)
-        if (imageData.a < 0.01)
+        if (imageData.a < 0.05)
             discard;
 
         #ifdef enabledFragDepth
@@ -119,7 +121,7 @@ void main() {
             gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
         #endif
     #elif defined(dRenderVariant_color)
-        if (imageData.a < 0.01)
+        if (imageData.a < 0.05)
             discard;
 
         gl_FragColor = imageData;

+ 1 - 6
src/mol-model/structure/model/properties/atomic/types.ts

@@ -15,12 +15,7 @@ export const enum Elements {
 }
 
 export const ElementNames: { [k: string]: string } = {
-    'H': 'Hydrogen',
-    'C': 'Carbon',
-    'N': 'Nitrogen',
-    'O': 'Oxygen',
-    'S': 'Sulfur',
-    'FE': 'Iron',
+    H: 'Hydrogen', HE: 'Helium', LI: 'Lithium', BE: 'Beryllium', B: 'Boron', C: 'Carbon', N: 'Nitrogen', O: 'Oxygen', F: 'Fluorine', NE: 'Neon', NA: 'Sodium', MG: 'Magnesium', AL: 'Aluminum', SI: 'Silicon', P: 'Phosphorus', S: 'Sulfur', CL: 'Chlorine', AR: 'Argon', K: 'Potassium', CA: 'Calcium', SC: 'Scandium', TI: 'Titanium', V: 'Vanadium', CR: 'Chromium', MN: 'Manganese', FE: 'Iron', CO: 'Cobalt', NI: 'Nickel', CU: 'Copper', ZN: 'Zinc', GA: 'Gallium', GE: 'Germanium', AS: 'Arsenic', SE: 'Selenium', BR: 'Bromine', KR: 'Krypton', RB: 'Rubidium', SR: 'Strontium', Y: 'Yttrium', ZR: 'Zirconium', NB: 'Niobium', MO: 'Molybdenum', TC: 'Technetium', RU: 'Ruthenium', RH: 'Rhodium', PD: 'Palladium', AG: 'Silver', CD: 'Cadmium', IN: 'Indium', SN: 'Tin', SB: 'Antimony', TE: 'Tellurium', I: 'Iodine', XE: 'Xenon', CS: 'Cesium', BA: 'Barium', LA: 'Lanthanum', CE: 'Cerium', PR: 'Praseodymium', ND: 'Neodymium', PM: 'Promethium', SM: 'Samarium', EU: 'Europium', GD: 'Gadolinium', TB: 'Terbium', DY: 'Dysprosium', HO: 'Holmium', ER: 'Erbium', TM: 'Thulium', YB: 'Ytterbium', LU: 'Lutetium', HF: 'Hafnium', TA: 'Tantalum', W: 'Wolfram', RE: 'Rhenium', OS: 'Osmium', IR: 'Iridium', PT: 'Platinum', AU: 'Gold', HG: 'Mercury', TL: 'Thallium', PB: 'Lead', BI: 'Bismuth', PO: 'Polonium', AT: 'Astatine', RN: 'Radon', FR: 'Francium', RA: 'Radium', AC: 'Actinium', TH: 'Thorium', PA: 'Protactinium', U: 'Uranium', NP: 'Neptunium', PU: 'Plutonium', AM: 'Americium', CM: 'Curium', BK: 'Berkelium', CF: 'Californium', ES: 'Einsteinium', FM: 'Fermium', MD: 'Mendelevium', NO: 'Nobelium', LR: 'Lawrencium', RF: 'Rutherfordium', DB: 'Dubnium', SG: 'Seaborgium', BH: 'Bohrium', HS: 'Hassium', MT: 'Meitnerium', DS: 'Darmstadtium', RG: 'Roentgenium', CN: 'Copernicium', NH: 'Nihonium', FL: 'Flerovium', MC: 'Moscovium', LV: 'Livermorium', TS: 'Tennessine', OG: 'Oganesson'
 };
 
 export const AlkaliMetals = new Set<ElementSymbol>(['LI', 'NA', 'K', 'RB', 'CS', 'FR'] as ElementSymbol[]);

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

@@ -429,7 +429,8 @@ function getUniqueElementSymbols(s: Structure) {
     const prop = StructureProperties.atom.type_symbol;
     const symbols = new Set<ElementSymbol>();
     const loc = StructureElement.Location.create(s);
-    for (const unit of s.units) {
+    for (const unitGroup of s.unitSymmetryGroups) {
+        const unit = unitGroup.units[0];
         if (!Unit.isAtomic(unit)) continue;
         loc.unit = unit;
         for (let i = 0, il = unit.elements.length; i < il; ++i) {

+ 3 - 2
src/mol-model/volume/volume.ts

@@ -7,7 +7,7 @@
 import { VolumeData, VolumeIsoValue } from './data';
 import { OrderedSet } from '../../mol-data/int';
 import { Sphere3D } from '../../mol-math/geometry';
-import { Vec3 } from '../../mol-math/linear-algebra';
+import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
 import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
 
 export namespace Volume {
@@ -86,7 +86,8 @@ export namespace Volume {
                 boundaryHelper.radiusPosition(tmpBoundaryPos);
             }
 
-            return boundaryHelper.getSphere(boundingSphere);
+            const bs = boundaryHelper.getSphere(boundingSphere);
+            return Sphere3D.expand(bs, bs, Mat4.getMaxScaleOnAxis(transform) * 10);
         }
     }
 }

+ 15 - 6
src/mol-plugin-state/builder/structure/representation-preset.ts

@@ -75,7 +75,8 @@ const auto = StructureRepresentationPresetProvider({
             case Structure.Size.Medium:
                 return polymerAndLigand.apply(ref, params, plugin);
             case Structure.Size.Small:
-                return atomicDetail.apply(ref, params, plugin);
+                // `showCarbohydrateSymbol: true` is nice e.g. for PDB 1aga
+                return atomicDetail.apply(ref, { ...params, showCarbohydrateSymbol: true }, plugin);
         }
     }
 });
@@ -105,7 +106,7 @@ const polymerAndLigand = StructureRepresentationPresetProvider({
             polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
             ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
             nonStandard: await presetStaticComponent(plugin, structureCell, 'non-standard'),
-            branched: await presetStaticComponent(plugin, structureCell, 'branched'),
+            branched: await presetStaticComponent(plugin, structureCell, 'branched', { label: 'Carbohydrate' }),
             water: await presetStaticComponent(plugin, structureCell, 'water'),
             coarse: await presetStaticComponent(plugin, structureCell, 'coarse')
         };
@@ -115,9 +116,9 @@ const polymerAndLigand = StructureRepresentationPresetProvider({
             polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams, color }, { tag: 'polymer' }),
             ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color }, { tag: 'ligand' }),
             nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color: color || 'polymer-id' }, { tag: 'non-standard' }),
-            branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.15 }, color }, { tag: 'branched-ball-and-stick' }),
+            branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.3 }, color }, { tag: 'branched-ball-and-stick' }),
             branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }, { tag: 'branched-snfg-3d' }),
-            water: builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.51 }, color }, { tag: 'water' }),
+            water: builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.6 }, color }, { tag: 'water' }),
             coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'polymer-id' }, { tag: 'coarse' })
         };
 
@@ -226,7 +227,10 @@ const atomicDetail = StructureRepresentationPresetProvider({
         name: 'Atomic Detail', group: BuiltInPresetGroupName,
         description: 'Shows everything in atomic detail with Ball & Stick.'
     },
-    params: () => CommonParams,
+    params: () => ({
+        ...CommonParams,
+        showCarbohydrateSymbol: PD.Boolean(false)
+    }),
     async apply(ref, params, plugin) {
         const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
         if (!structureCell) return {};
@@ -237,8 +241,13 @@ const atomicDetail = StructureRepresentationPresetProvider({
 
         const { update, builder, typeParams, color } = reprBuilder(plugin, params);
         const representations = {
-            all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color }, { tag: 'all' })
+            all: builder.buildRepresentation(update, components.all, { type: 'ball-and-stick', typeParams, color }, { tag: 'all' }),
         };
+        if (params.showCarbohydrateSymbol) {
+            Object.assign(representations, {
+                snfg3d: builder.buildRepresentation(update, components.all, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color }, { tag: 'snfg-3d' }),
+            });
+        }
 
         await update.commit({ revertOnError: true });
         return { components, representations };

+ 89 - 16
src/mol-plugin-state/helpers/structure-selection-query.ts

@@ -6,8 +6,8 @@
  */
 
 import { CustomProperty } from '../../mol-model-props/common/custom-property';
-import { QueryContext, Structure, StructureQuery, StructureSelection } from '../../mol-model/structure';
-import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType } from '../../mol-model/structure/model/types';
+import { QueryContext, Structure, StructureQuery, StructureSelection, StructureProperties, StructureElement } from '../../mol-model/structure';
+import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType, AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol } from '../../mol-model/structure/model/types';
 import { PluginContext } from '../../mol-plugin/context';
 import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
 import Expression from '../../mol-script/language/expression';
@@ -15,9 +15,9 @@ import { compile } from '../../mol-script/runtime/query/compiler';
 import { StateBuilder } from '../../mol-state';
 import { RuntimeContext } from '../../mol-task';
 import { SetUtils } from '../../mol-util/set';
-import { stringToWords } from '../../mol-util/string';
 import { PluginStateObject } from '../objects';
 import { StateTransforms } from '../transforms';
+import { ElementNames } from '../../mol-model/structure/model/properties/atomic/types';
 
 export enum StructureSelectionCategory {
     Type = 'Type',
@@ -41,6 +41,7 @@ interface StructureSelectionQuery {
     readonly description: string
     readonly category: string
     readonly isHidden: boolean
+    readonly priority: number
     readonly referencesCurrent: boolean
     readonly query: StructureQuery
     readonly ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
@@ -51,6 +52,7 @@ interface StructureSelectionQueryProps {
     description?: string
     category?: string
     isHidden?: boolean
+    priority?: number
     referencesCurrent?: boolean
     ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
 }
@@ -63,6 +65,7 @@ function StructureSelectionQuery(label: string, expression: Expression, props: S
         description: props.description || '',
         category: props.category ?? StructureSelectionCategory.Misc,
         isHidden: !!props.isHidden,
+        priority: props.priority || 0,
         referencesCurrent: !!props.referencesCurrent,
         get query() {
             if (!_query) _query = compile<StructureSelection>(expression);
@@ -81,7 +84,7 @@ function StructureSelectionQuery(label: string, expression: Expression, props: S
     };
 }
 
-const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '' });
+const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '', priority: 1000 });
 const current = StructureSelectionQuery('Current Selection', MS.internal.generator.current(), { category: '', referencesCurrent: true });
 
 const polymer = StructureSelectionQuery('Polymer', MS.struct.modifier.union([
@@ -426,20 +429,96 @@ const StandardNucleicBases = [
     [['N', 'DN'], 'UNKNOWN'],
 ].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
 
-export function ResidueQuery([names, label]: [string[], string], category: string) {
-    return StructureSelectionQuery(`${stringToWords(label)} (${names.join(', ')})`, MS.struct.modifier.union([
+export function ResidueQuery([names, label]: [string[], string], category: string, priority = 0) {
+    return StructureSelectionQuery(`${label} (${names.join(', ')})`, MS.struct.modifier.union([
         MS.struct.generator.atomGroups({
             'residue-test': MS.core.set.has([MS.set(...names), MS.ammp('auth_comp_id')])
         })
-    ]), { category });
+    ]), { category, priority, description: label });
 }
 
-export function ElementSymbolQuery([names, label]: [string[], string], category: string) {
-    return StructureSelectionQuery(`${stringToWords(label)} (${names.join(', ')})`, MS.struct.modifier.union([
+export function ElementSymbolQuery([names, label]: [string[], string], category: string, priority: number) {
+    return StructureSelectionQuery(`${label} (${names.join(', ')})`, MS.struct.modifier.union([
         MS.struct.generator.atomGroups({
             'atom-test': MS.core.set.has([MS.set(...names), MS.acp('elementSymbol')])
         })
-    ]), { category });
+    ]), { category, priority });
+}
+
+export function EntityDescriptionQuery([description, label]: [string[], string], category: string, priority: number) {
+    return StructureSelectionQuery(`${label}`, MS.struct.modifier.union([
+        MS.struct.generator.atomGroups({
+            'entity-test': MS.core.list.equal([MS.list(...description), MS.ammp('entityDescription')])
+        })
+    ]), { category, priority, description: description.join(', ') });
+}
+
+const StandardResidues = SetUtils.unionMany(
+    AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames
+);
+
+export function getElementQueries(structures: Structure[]) {
+    const uniqueElements = new Set<ElementSymbol>();
+    for (const structure of structures) {
+        structure.uniqueElementSymbols.forEach(e => uniqueElements.add(e));
+    }
+
+    const queries: StructureSelectionQuery[] = [];
+    uniqueElements.forEach(e => {
+        const label = ElementNames[e] || e;
+        queries.push(ElementSymbolQuery([[e], label], 'Element Symbol', 0));
+    });
+    return queries;
+}
+
+export function getNonStandardResidueQueries(structures: Structure[]) {
+    const residueLabels = new Map<string, string>();
+    const uniqueResidues = new Set<string>();
+    for (const structure of structures) {
+        structure.uniqueResidueNames.forEach(r => uniqueResidues.add(r));
+        for (const m of structure.models) {
+            structure.uniqueResidueNames.forEach(r => {
+                const comp = m.properties.chemicalComponentMap.get(r);
+                if (comp) residueLabels.set(r, comp.name);
+            });
+        }
+    }
+
+    const queries: StructureSelectionQuery[] = [];
+    SetUtils.difference(uniqueResidues, StandardResidues).forEach(r => {
+        const label = residueLabels.get(r) || r;
+        queries.push(ResidueQuery([[r], label], 'Ligand/Non-standard Residue', 200));
+    });
+    return queries;
+}
+
+export function getPolymerAndBranchedEntityQueries(structures: Structure[]) {
+    const uniqueEntities = new Map<string, string[]>();
+    const l = StructureElement.Location.create();
+    for (const structure of structures) {
+        l.structure = structure;
+        for (const ug of structure.unitSymmetryGroups) {
+            l.unit = ug.units[0];
+            l.element = ug.elements[0];
+            const entityType = StructureProperties.entity.type(l);
+            if (entityType === 'polymer' || entityType === 'branched') {
+                const description = StructureProperties.entity.pdbx_description(l);
+                uniqueEntities.set(description.join(', '), description);
+            }
+        }
+    }
+
+    const queries: StructureSelectionQuery[] = [];
+    uniqueEntities.forEach((v, k) => {
+        queries.push(EntityDescriptionQuery([v, k], 'Polymer/Carbohydrate Entities', 300));
+    });
+    return queries;
+}
+
+export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
+    return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
+        { expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
+        { tags: customTag ? [query, customTag] : [query] });
 }
 
 export const StructureSelectionQueries = {
@@ -471,12 +550,6 @@ export const StructureSelectionQueries = {
     wholeResidues,
 };
 
-export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
-    return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
-        { expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
-        { tags: customTag ? [query, customTag] : [query] });
-}
-
 export class StructureSelectionQueryRegistry {
     list: StructureSelectionQuery[] = []
     options: [StructureSelectionQuery, string, string][] = []

+ 2 - 1
src/mol-plugin-ui/controls/action-menu.tsx

@@ -20,7 +20,7 @@ export class ActionMenu extends React.PureComponent<ActionMenu.Props> {
         const cmd = this.props;
         const section = <Section items={cmd.items} onSelect={cmd.onSelect} current={cmd.current} multiselect={this.props.multiselect} noOffset={this.props.noOffset} noAccent={this.props.noAccent} />;
         return <div className={`msp-action-menu-options${cmd.header ? '' : ' msp-action-menu-options-no-header'}`}>
-            {cmd.header && <ControlGroup header={cmd.header} initialExpanded={true} hideExpander={true} hideOffset onHeaderClick={this.hide} topRightIcon={Close}>
+            {cmd.header && <ControlGroup header={cmd.header} title={cmd.title} initialExpanded={true} hideExpander={true} hideOffset onHeaderClick={this.hide} topRightIcon={Close}>
                 {section}
             </ControlGroup>}
             {!cmd.header && section}
@@ -33,6 +33,7 @@ export namespace ActionMenu {
         items: Items,
         onSelect: OnSelect | OnSelectMany,
         header?: string,
+        title?: string,
         current?: Item,
         multiselect?: boolean,
         noOffset?: boolean,

+ 2 - 1
src/mol-plugin-ui/controls/common.tsx

@@ -16,6 +16,7 @@ export type ColorAccent = 'cyan' | 'red' | 'gray' | 'green' | 'purple' | 'blue'
 
 export class ControlGroup extends React.Component<{
     header: string,
+    title?: string,
     initialExpanded?: boolean,
     hideExpander?: boolean,
     hideOffset?: boolean,
@@ -41,7 +42,7 @@ export class ControlGroup extends React.Component<{
 
         // TODO: customize header style (bg color, togle button etc)
         return <div className='msp-control-group-wrapper' style={{ position: 'relative', marginTop: this.props.noTopMargin ? 0 : void 0 }}>
-            <div className='msp-control-group-header' style={{ marginLeft: this.props.headerLeftMargin }}>
+            <div className='msp-control-group-header' style={{ marginLeft: this.props.headerLeftMargin }} title={this.props.title}>
                 <Button onClick={this.headerClicked}>
                     {!this.props.hideExpander && <Icon svg={this.state.isExpanded ? ArrowRight : ArrowDropDown} />}
                     {this.props.topRightIcon && <Icon svg={this.props.topRightIcon} style={{ position: 'absolute', right: '2px', top: 0 }} />}

+ 8 - 11
src/mol-plugin-ui/skin/base/components/controls-base.scss

@@ -88,21 +88,18 @@
     outline: none;
 }
 
-// .msp-btn-icon, .msp-btn-icon-small {
-//     svg {
-//         display: inline-flex;
-//         vertical-align: middle;
-//         font-size: 1rem;
-//         margin-bottom: 3px;
-//     }
-// }
-
 .msp-material-icon {
     svg {
         display: inline-flex;
         vertical-align: middle;
-        font-size: 1rem;
+        font-size: 1.2em;
         margin-bottom: 3px;
+
+        fill: currentColor;
+        width: 1em;
+        height: 1em;
+        flex-shrink: 0;
+        user-select: none;
     }
 }
 
@@ -270,7 +267,7 @@ select[multiple],
 select[size] {
     height: auto;
 }
-  
+
 // Reset height for `textarea`s
 textarea.msp-form-control {
     height: auto;

+ 25 - 57
src/mol-plugin-ui/structure/selection.tsx

@@ -11,7 +11,7 @@ import Brush from '@material-ui/icons/Brush';
 import Restore from '@material-ui/icons/Restore';
 import Remove from '@material-ui/icons/Remove';
 import * as React from 'react';
-import { StructureSelectionQueries, StructureSelectionQuery, ResidueQuery, ElementSymbolQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
+import { StructureSelectionQueries, StructureSelectionQuery, getNonStandardResidueQueries, getElementQueries, getPolymerAndBranchedEntityQueries } from '../../mol-plugin-state/helpers/structure-selection-query';
 import { InteractivityManager } from '../../mol-plugin-state/manager/interactivity';
 import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component';
 import { StructureRef, StructureComponentRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
@@ -25,9 +25,7 @@ import { Button, ControlGroup, IconButton, ToggleButton } from '../controls/comm
 import { ParameterControls, ParamOnChange, PureSelectControl } from '../controls/parameters';
 import { Union, Subtract, Intersect, SetSvg as SetSvg, CubeSvg } from '../controls/icons';
 import { AddComponentControls } from './components';
-import { SetUtils } from '../../mol-util/set';
-import { AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol } from '../../mol-model/structure/model/types';
-import { ElementNames } from '../../mol-model/structure/model/properties/atomic/types';
+import Structure from '../../mol-model/structure/structure/structure';
 
 const StructureSelectionParams = {
     granularity: InteractivityManager.Params.granularity,
@@ -48,10 +46,6 @@ const ActionHeader = new Map<StructureSelectionModifier, string>([
     ['set', 'Set Selection']
 ] as const);
 
-const StandardResidues = SetUtils.unionMany(
-    AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames
-);
-
 export class StructureSelectionActionsControls extends PluginUIComponent<{}, StructureSelectionActionsControlsState> {
     state = {
         action: void 0 as StructureSelectionActionsControlsState['action'],
@@ -108,45 +102,13 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
         }
     }
 
-    get elementQueries () {
-        const uniqueElements = new Set<ElementSymbol>();
-        for (const s of this.plugin.managers.structure.hierarchy.selection.structures) {
-            const structure = s.cell.obj?.data;
-            if (!structure) continue;
-
-            structure.uniqueElementSymbols.forEach(e => uniqueElements.add(e));
-        }
-
-        const queries: StructureSelectionQuery[] = [];
-        uniqueElements.forEach(e => {
-            const label = ElementNames[e] || e;
-            queries.push(ElementSymbolQuery([[e], label], 'Element Symbol'));
-        });
-        return queries;
-    }
-
-    get nonStandardResidueQueries () {
-        const residueLabels = new Map<string, string>();
-        const uniqueResidues = new Set<string>();
+    get structures () {
+        const structures: Structure[] = [];
         for (const s of this.plugin.managers.structure.hierarchy.selection.structures) {
             const structure = s.cell.obj?.data;
-            if (!structure) continue;
-
-            structure.uniqueResidueNames.forEach(r => uniqueResidues.add(r));
-            for (const m of structure.models) {
-                structure.uniqueResidueNames.forEach(r => {
-                    const comp = m.properties.chemicalComponentMap.get(r);
-                    if (comp) residueLabels.set(r, comp.name);
-                });
-            }
+            if (structure) structures.push(structure);
         }
-
-        const queries: StructureSelectionQuery[] = [];
-        SetUtils.difference(uniqueResidues, StandardResidues).forEach(r => {
-            const label = residueLabels.get(r) || r;
-            queries.push(ResidueQuery([[r], label], 'Ligand/Non-standard Residue'));
-        });
-        return queries;
+        return structures;
     }
 
     private queriesItems: ActionMenu.Items[] = []
@@ -154,7 +116,13 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
     get queries () {
         const { registry } = this.plugin.query.structure;
         if (registry.version !== this.queriesVersion) {
-            const queries = [...registry.list, ...this.nonStandardResidueQueries, ...this.elementQueries];
+            const structures = this.structures;
+            const queries = [
+                ...registry.list,
+                ...getPolymerAndBranchedEntityQueries(structures),
+                ...getNonStandardResidueQueries(structures),
+                ...getElementQueries(structures)
+            ].sort((a, b) => b.priority - a.priority);
             this.queriesItems = ActionMenu.createItems(queries, {
                 filter: q => q !== StructureSelectionQueries.current && !q.isHidden,
                 label: q => q.label,
@@ -204,29 +172,29 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
 
         return <>
             <div className='msp-flex-row' style={{ background: 'none' }}>
-                <PureSelectControl title={`Picking Level`} param={StructureSelectionParams.granularity} name='granularity' value={granularity} onChange={this.setGranuality} isDisabled={this.isDisabled} />
-                <ToggleButton icon={Union} title={ActionHeader.get('add')} toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
-                <ToggleButton icon={Subtract} title={ActionHeader.get('remove')} toggle={this.toggleRemove} isSelected={this.state.action === 'remove'} disabled={this.isDisabled} />
-                <ToggleButton icon={Intersect} title={ActionHeader.get('intersect')} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
-                <ToggleButton icon={SetSvg} title={ActionHeader.get('set')} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
-
-                <ToggleButton icon={Brush} title='Color' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} style={{ marginLeft: '10px' }}  />
-                <ToggleButton icon={CubeSvg} title='Create Representation' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
-                <IconButton svg={Remove} title='Subtract from Representations' onClick={this.subtract} disabled={this.isDisabled} />
+                <PureSelectControl title={`Picking Level for selecting and highlighting`} param={StructureSelectionParams.granularity} name='granularity' value={granularity} onChange={this.setGranuality} isDisabled={this.isDisabled} />
+                <ToggleButton icon={Union} title={`${ActionHeader.get('add')}. Hold shift key to keep menu open.`} toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
+                <ToggleButton icon={Subtract} title={`${ActionHeader.get('remove')}. Hold shift key to keep menu open.`} toggle={this.toggleRemove} isSelected={this.state.action === 'remove'} disabled={this.isDisabled} />
+                <ToggleButton icon={Intersect} title={`${ActionHeader.get('intersect')}. Hold shift key to keep menu open.`} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
+                <ToggleButton icon={SetSvg} title={`${ActionHeader.get('set')}. Hold shift key to keep menu open.`} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
+
+                <ToggleButton icon={Brush} title='Color Selection' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} style={{ marginLeft: '10px' }}  />
+                <ToggleButton icon={CubeSvg} title='Create Representation of Selection' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
+                <IconButton svg={Remove} title='Subtract Selection from Representations' onClick={this.subtract} disabled={this.isDisabled} />
                 <IconButton svg={Restore} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} title={undoTitle} />
 
                 <IconButton svg={CancelOutlined} title='Turn selection mode off' onClick={this.turnOff} style={{ marginLeft: '10px' }} />
             </div>
             {(this.state.action && this.state.action !== 'color' && this.state.action !== 'add-repr') && <div className='msp-selection-viewport-controls-actions'>
-                <ActionMenu header={ActionHeader.get(this.state.action as StructureSelectionModifier)} items={this.queries} onSelect={this.selectQuery} noOffset />
+                <ActionMenu header={ActionHeader.get(this.state.action as StructureSelectionModifier)} title='Click to close.' items={this.queries} onSelect={this.selectQuery} noOffset />
             </div>}
             {this.state.action === 'color' && <div className='msp-selection-viewport-controls-actions'>
-                <ControlGroup header='Color' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleColor} topRightIcon={Close}>
+                <ControlGroup header='Color' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleColor} topRightIcon={Close}>
                     <ApplyColorControls onApply={this.toggleColor} />
                 </ControlGroup>
             </div>}
             {this.state.action === 'add-repr' && <div className='msp-selection-viewport-controls-actions'>
-                <ControlGroup header='Add Representation' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleAddRepr} topRightIcon={Close}>
+                <ControlGroup header='Add Representation' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleAddRepr} topRightIcon={Close}>
                     <AddComponentControls onApply={this.toggleAddRepr} forSelection />
                 </ControlGroup>
             </div>}

+ 4 - 4
src/mol-plugin-ui/viewport.tsx

@@ -104,8 +104,8 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
                 </div>
                 <div>
                     <div className='msp-semi-transparent-background' />
-                    {this.icon(BuildOutlined, this.toggleControls, 'Toggle Controls', this.plugin.layout.state.showControls)}
-                    {this.plugin.config.get(PluginConfig.Viewport.ShowExpand) && this.icon(Fullscreen, this.toggleExpanded, 'Toggle Expanded', this.plugin.layout.state.isExpanded)}
+                    {this.icon(BuildOutlined, this.toggleControls, 'Toggle Controls Panel', this.plugin.layout.state.showControls)}
+                    {this.plugin.config.get(PluginConfig.Viewport.ShowExpand) && this.icon(Fullscreen, this.toggleExpanded, 'Toggle Expanded Viewport', this.plugin.layout.state.isExpanded)}
                     {this.icon(Tune, this.toggleSettingsExpanded, 'Settings / Controls Info', this.state.isSettingsExpanded)}
                 </div>
                 {this.plugin.config.get(PluginConfig.Viewport.ShowSelectionMode) && <div>
@@ -114,13 +114,13 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
                 </div>}
             </div>
             {this.state.isScreenshotExpanded && <div className='msp-viewport-controls-panel'>
-                <ControlGroup header='Screenshot / State' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleScreenshotExpanded}
+                <ControlGroup header='Screenshot / State' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleScreenshotExpanded}
                     topRightIcon={Close} noTopMargin childrenClassName='msp-viewport-controls-panel-controls'>
                     <DownloadScreenshotControls close={this.toggleScreenshotExpanded} />
                 </ControlGroup>
             </div>}
             {this.state.isSettingsExpanded && <div className='msp-viewport-controls-panel'>
-                <ControlGroup header='Settings / Controls Info' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleSettingsExpanded}
+                <ControlGroup header='Settings / Controls Info' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleSettingsExpanded}
                     topRightIcon={Close} noTopMargin childrenClassName='msp-viewport-controls-panel-controls'>
                     <SimpleSettingsControl />
                 </ControlGroup>

+ 2 - 1
src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts

@@ -26,6 +26,7 @@ import { EmptyLoci, Loci } from '../../../mol-model/loci';
 import { VisualContext } from '../../../mol-repr/visual';
 import { Theme } from '../../../mol-theme/theme';
 import { getAltResidueLociFromId } from './util/common';
+import { BaseGeometry } from '../../../mol-geo/geometry/base';
 
 const t = Mat4.identity();
 const sVec = Vec3.zero();
@@ -162,7 +163,7 @@ function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure,
 
 export const CarbohydrateSymbolParams = {
     ...ComplexMeshParams,
-    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
+    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }, BaseGeometry.CustomQualityParamInfo),
     sizeFactor: PD.Numeric(1.75, { min: 0, max: 10, step: 0.01 }),
 };
 export type CarbohydrateSymbolParams = typeof CarbohydrateSymbolParams

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

@@ -10,12 +10,13 @@ import { UnitsMeshParams, UnitsSpheresParams, UnitsVisual, UnitsSpheresVisual, U
 import { WebGLContext } from '../../../mol-gl/webgl/context';
 import { createElementSphereImpostor, ElementIterator, getElementLoci, eachElement, createElementSphereMesh } from './util/element';
 import { VisualUpdateState } from '../../util';
+import { BaseGeometry } from '../../../mol-geo/geometry/base';
 
 export const ElementSphereParams = {
     ...UnitsMeshParams,
     ...UnitsSpheresParams,
     sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
-    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
+    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }, BaseGeometry.CustomQualityParamInfo),
     ignoreHydrogens: PD.Boolean(false),
     traceOnly: PD.Boolean(false),
 };

+ 2 - 1
src/mol-repr/structure/visual/ellipsoid-mesh.ts

@@ -21,11 +21,12 @@ import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/property
 import { equalEps } from '../../../mol-math/linear-algebra/3d/common';
 import { addSphere } from '../../../mol-geo/geometry/mesh/builder/sphere';
 import { Sphere3D } from '../../../mol-math/geometry';
+import { BaseGeometry } from '../../../mol-geo/geometry/base';
 
 export const EllipsoidMeshParams = {
     ...UnitsMeshParams,
     sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
-    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
+    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }, BaseGeometry.CustomQualityParamInfo),
     ignoreHydrogens: PD.Boolean(false),
 };
 export type EllipsoidMeshParams = typeof EllipsoidMeshParams

+ 3 - 2
src/mol-repr/structure/visual/orientation-ellipsoid-mesh.ts

@@ -21,11 +21,12 @@ import { EmptyLoci, Loci } from '../../../mol-model/loci';
 import { UnitIndex } from '../../../mol-model/structure/structure/element/element';
 import { LocationIterator } from '../../../mol-geo/util/location-iterator';
 import { MoleculeType } from '../../../mol-model/structure/model/types';
+import { BaseGeometry } from '../../../mol-geo/geometry/base';
 
 export const OrientationEllipsoidMeshParams = {
     ...UnitsMeshParams,
     sizeFactor: PD.Numeric(1, { min: 0, max: 2, step: 0.1 }),
-    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
+    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }, BaseGeometry.CustomQualityParamInfo),
 };
 export type OrientationEllipsoidMeshParams = typeof OrientationEllipsoidMeshParams
 
@@ -80,7 +81,7 @@ export function createOrientationEllipsoidMesh(ctx: VisualContext, unit: Unit, s
     const radiusScale = Vec3.create(size[2], size[1], size[0]);
 
     builderState.currentGroup = 0;
-    addEllipsoid(builderState, origin, dirA, dirB, radiusScale, detail);
+    addEllipsoid(builderState, origin, dirA, dirB, radiusScale, detail + 1);
 
     const m = MeshBuilder.getMesh(builderState);
 

+ 5 - 0
src/mol-repr/util.ts

@@ -51,6 +51,7 @@ export interface QualityProps {
     linearSegments: number
     resolution: number
     doubleSided: boolean
+    alpha: number
 }
 
 export const DefaultQualityThresholds = {
@@ -151,6 +152,10 @@ export function getQualityProps(props: Partial<QualityProps>, data?: any) {
             break;
     }
 
+    if (props.alpha !== undefined && props.alpha < 1) {
+        doubleSided = false;
+    }
+
     return {
         detail,
         radialSegments,

+ 2 - 1
src/mol-script/language/symbol-table/core.ts

@@ -145,7 +145,8 @@ const str = {
 
 const list = {
     '@header': 'Lists',
-    getAt: symbol(Arguments.Dictionary({ 0: Argument(Types.List()), 1: Argument(Type.Num) }), Types.AnyVar)
+    getAt: symbol(Arguments.Dictionary({ 0: Argument(Types.List()), 1: Argument(Type.Num) }), Types.AnyVar),
+    equal: symbol(Arguments.Dictionary({ 0: Argument(Types.List()), 1: Argument(Types.List()) }), Type.Bool)
 };
 
 const set = {

+ 1 - 0
src/mol-script/language/symbol-table/structure-query.ts

@@ -308,6 +308,7 @@ const atomProperty = {
 
         entityType: atomProp(Types.EntityType, 'Type of the entity as defined in mmCIF (polymer, non-polymer, branched, water)'),
         entitySubtype: atomProp(Types.EntitySubtype, 'Subtype of the entity as defined in mmCIF _entity_poly.type and _pdbx_entity_branch.type (other, polypeptide(D), polypeptide(L), polydeoxyribonucleotide, polyribonucleotide, polydeoxyribonucleotide/polyribonucleotide hybrid, cyclic-pseudo-peptide, peptide nucleic acid, oligosaccharide)'),
+        entityDescription: atomProp(Core.Types.List(Type.Str)),
         objectPrimitive: atomProp(Types.ObjectPrimitive, 'Type of the primitive object used to model this segment as defined in mmCIF/IHM (atomistic, sphere, gaussian, other)'),
 
         secondaryStructureKey: atomProp(Type.AnyValue, 'Unique value for each secondary structure element.'),

+ 3 - 0
src/mol-script/runtime/query/table.ts

@@ -14,6 +14,7 @@ import { upperCaseAny } from '../../../mol-util/string';
 import { VdwRadius, AtomWeight, AtomNumber } from '../../../mol-model/structure/model/properties/atomic';
 import { cantorPairing } from '../../../mol-data/util';
 import { bundleElementImpl, bundleGenerator } from '../../../mol-model/structure/query/queries/internal';
+import { arrayEqual } from '../../../mol-util/array';
 
 const C = QuerySymbolRuntime.Const;
 const D = QuerySymbolRuntime.Dynamic;
@@ -151,6 +152,7 @@ const symbols = [
 
     // ============= LIST ================
     C(MolScript.core.list.getAt, (ctx, v) => v[0](ctx)[v[1](ctx)]),
+    C(MolScript.core.list.equal, (ctx, v) => arrayEqual(v[0](ctx), v[1](ctx))),
 
     // ============= SET ================
     C(MolScript.core.set.has, function core_set_has(ctx, v) { return v[0](ctx).has(v[1](ctx)); }),
@@ -334,6 +336,7 @@ const symbols = [
 
     D(MolScript.structureQuery.atomProperty.macromolecular.entityType, atomProp(StructureProperties.entity.type)),
     D(MolScript.structureQuery.atomProperty.macromolecular.entitySubtype, atomProp(StructureProperties.entity.subtype)),
+    D(MolScript.structureQuery.atomProperty.macromolecular.entityDescription, atomProp(StructureProperties.entity.pdbx_description)),
     D(MolScript.structureQuery.atomProperty.macromolecular.objectPrimitive, atomProp(StructureProperties.unit.object_primitive)),
 
     D(MolScript.structureQuery.atomProperty.macromolecular.isNonStandard, atomProp(StructureProperties.residue.isNonStandard)),

+ 1 - 0
src/mol-script/script/mol-script/symbols.ts

@@ -234,6 +234,7 @@ export const SymbolTable = [
             Alias(MolScript.structureQuery.atomProperty.macromolecular.B_iso_or_equiv, 'atom.B_iso_or_equiv', 'atom.bfactor'),
             Alias(MolScript.structureQuery.atomProperty.macromolecular.entityType, 'atom.entity-type'),
             Alias(MolScript.structureQuery.atomProperty.macromolecular.entitySubtype, 'atom.entity-subtype'),
+            Alias(MolScript.structureQuery.atomProperty.macromolecular.entityDescription, 'atom.entity-description'),
             Alias(MolScript.structureQuery.atomProperty.macromolecular.objectPrimitive, 'atom.object-primitive'),
             Alias(MolScript.structureQuery.atomProperty.macromolecular.chemCompType, 'atom.chem-comp-type'),
 

+ 1 - 1
src/mol-util/array.ts

@@ -105,7 +105,7 @@ export function arraySetRemove<T>(xs: T[], x: T) {
     return true;
 }
 
-export function arrayEqual<T>(xs?: T[], ys?: T[]) {
+export function arrayEqual<T>(xs?: ArrayLike<T>, ys?: ArrayLike<T>) {
     if (!xs || xs.length === 0) return !ys || ys.length === 0;
     if (!ys) return false;
 

Some files were not shown because too many files changed in this diff