瀏覽代碼

Merge pull request #352 from molstar/var-fixes

Various fixes
David Sehnal 3 年之前
父節點
當前提交
b920053349

+ 5 - 0
CHANGELOG.md

@@ -6,6 +6,11 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Fix marking pass not working with ``transparentBackground``
+- Fix pdbe xray maps url not https
+- Fix entity-id color theme broken for non-IHM models
+- Improve/fix marking of ``InteractionsInterUnitVisual`` (mark when all contact-feature members are given)
+- Add missing "entity-id" and "enity-source" options for carbon coloring to "element-symbol" color theme
 - Fix VolumeServer/query CLI
 - Support automatic iso-value adjustment for VolumeServer data in ``Viewer.loadVolumeFromUrl``
 

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

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 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>
@@ -310,12 +310,12 @@ export class DrawPass {
             const markingDepthTest = props.marking.ghostEdgeStrength < 1;
             if (markingDepthTest) {
                 this.marking.depthTarget.bind();
-                renderer.clear(false);
+                renderer.clear(false, true);
                 renderer.renderMarkingDepth(scene.primitives, camera, null);
             }
 
             this.marking.maskTarget.bind();
-            renderer.clear(false);
+            renderer.clear(false, true);
             renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
 
             this.marking.update(props.marking);

+ 4 - 4
src/mol-gl/renderer.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -53,7 +53,7 @@ interface Renderer {
     readonly stats: RendererStats
     readonly props: Readonly<RendererProps>
 
-    clear: (toBackgroundColor: boolean) => void
+    clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
     clearDepth: () => void
     update: (camera: ICamera) => void
 
@@ -523,13 +523,13 @@ namespace Renderer {
         };
 
         return {
-            clear: (toBackgroundColor: boolean) => {
+            clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => {
                 state.enable(gl.SCISSOR_TEST);
                 state.enable(gl.DEPTH_TEST);
                 state.colorMask(true, true, true, true);
                 state.depthMask(true);
 
-                if (transparentBackground) {
+                if (transparentBackground && !ignoreTransparentBackground) {
                     state.clearColor(0, 0, 0, 0);
                 } else if (toBackgroundColor) {
                     state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1);

+ 15 - 24
src/mol-model-props/computed/interactions/common.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -98,29 +98,20 @@ class InteractionsInterContacts extends InterUnitGraph<number, Features.FeatureI
     constructor(map: Map<number, InterUnitGraph.UnitPairEdges<number, Features.FeatureIndex, InteractionsInterContacts.Props>[]>, unitsFeatures: IntMap<Features>) {
         super(map);
 
-        let count = 0;
-        const elementKeyIndex = new Map<string, number[]>();
-
-        const add = (index: StructureElement.UnitIndex, unitId: number) => {
-            const vertexKey = this.getElementKey(index, unitId);
-            const e = elementKeyIndex.get(vertexKey);
-            if (e === undefined) elementKeyIndex.set(vertexKey, [count]);
-            else e.push(count);
-        };
-
-        this.map.forEach(pairEdgesArray => {
-            pairEdgesArray.forEach(({ unitA, connectedIndices }) => {
-                connectedIndices.forEach(indexA => {
-                    const { offsets: offsetsA, members: membersA } = unitsFeatures.get(unitA);
-                    for (let j = offsetsA[indexA], jl = offsetsA[indexA + 1]; j < jl; ++j) {
-                        add(membersA[j], unitA);
-                    }
-                    count += 1;
-                });
-            });
-        });
-
-        this.elementKeyIndex = elementKeyIndex;
+        this.elementKeyIndex = new Map<string, number[]>();
+        for (let i = 0, il = this.edges.length; i < il; ++i) {
+            const { unitA, indexA } = this.edges[i];
+            const { offsets, members } = unitsFeatures.get(unitA);
+            for (let j = offsets[indexA], jl = offsets[indexA + 1]; j < jl; ++j) {
+                const vertexKey = this.getElementKey(members[j], unitA);
+                const e = this.elementKeyIndex.get(vertexKey);
+                if (e === undefined) {
+                    this.elementKeyIndex.set(vertexKey, [i]);
+                } else {
+                    e.push(i);
+                }
+            }
+        }
     }
 }
 namespace InteractionsInterContacts {

+ 41 - 10
src/mol-model-props/computed/representations/interactions-inter-unit-cylinder.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -74,10 +74,11 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
                 if (!childUnitA) return true;
 
                 const unitA = structure.unitMap.get(b.unitA);
-                const fA = unitsFeatures.get(b.unitA);
-                // TODO: check all members
-                const eA = unitA.elements[fA.members[fA.offsets[b.indexA]]];
-                if (!SortedArray.has(childUnitA.elements, eA)) return true;
+                const { offsets, members } = unitsFeatures.get(b.unitA);
+                for (let i = offsets[b.indexA], il = offsets[b.indexA + 1]; i < il; ++i) {
+                    const eA = unitA.elements[members[i]];
+                    if (!SortedArray.has(childUnitA.elements, eA)) return true;
+                }
             }
 
             return false;
@@ -144,6 +145,9 @@ function getInteractionLoci(pickingId: PickingId, structure: Structure, id: numb
     return EmptyLoci;
 }
 
+const __unitMap = new Map<number, OrderedSet<StructureElement.UnitIndex>>();
+const __contactIndicesSet = new Set<number>();
+
 function eachInteraction(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean, isMarking: boolean) {
     let changed = false;
     if (Interactions.isLoci(loci)) {
@@ -162,21 +166,48 @@ function eachInteraction(loci: Loci, structure: Structure, apply: (interval: Int
         if (!Structure.areEquivalent(loci.structure, structure)) return false;
         if (isMarking && loci.elements.length === 1) return false; // only a single unit
 
-        const contacts = InteractionsProvider.get(structure).value?.contacts;
-        if (!contacts) return false;
+        const interactions = InteractionsProvider.get(structure).value;
+        if (!interactions) return false;
+
+        const { contacts, unitsFeatures } = interactions;
+
+        for (const e of loci.elements) __unitMap.set(e.unit.id, e.indices);
 
-        // TODO when isMarking, all elements of contact features need to be in the loci
         for (const e of loci.elements) {
             const { unit } = e;
             if (!Unit.isAtomic(unit)) continue;
-            if (isMarking && OrderedSet.size(e.indices) === 1) continue;
 
             OrderedSet.forEach(e.indices, v => {
                 for (const idx of contacts.getContactIndicesForElement(v, unit)) {
-                    if (apply(Interval.ofSingleton(idx))) changed = true;
+                    __contactIndicesSet.add(idx);
                 }
             });
         }
+
+        __contactIndicesSet.forEach(i => {
+            if (isMarking) {
+                const { indexA, unitA, indexB, unitB } = contacts.edges[i];
+
+                const indicesA = __unitMap.get(unitA);
+                const indicesB = __unitMap.get(unitB);
+                if (!indicesA || !indicesB) return;
+
+                const { offsets: offsetsA, members: membersA } = unitsFeatures.get(unitA);
+                for (let j = offsetsA[indexA], jl = offsetsA[indexA + 1]; j < jl; ++j) {
+                    if (!OrderedSet.has(indicesA, membersA[j])) return;
+                }
+
+                const { offsets: offsetsB, members: membersB } = unitsFeatures.get(unitB);
+                for (let j = offsetsB[indexB], jl = offsetsB[indexB + 1]; j < jl; ++j) {
+                    if (!OrderedSet.has(indicesB, membersB[j])) return;
+                }
+            }
+
+            if (apply(Interval.ofSingleton(i))) changed = true;
+        });
+
+        __unitMap.clear();
+        __contactIndicesSet.clear();
     }
     return changed;
 }

+ 15 - 6
src/mol-model-props/computed/representations/interactions-intra-unit-cylinder.ts

@@ -56,11 +56,19 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
             const sizeB = theme.size.size(location);
             return Math.min(sizeA, sizeB) * sizeFactor;
         },
-        ignore: (edgeIndex: number) => (
-            flag[edgeIndex] === InteractionFlag.Filtered ||
-            // TODO: check all members
-            (!!childUnit && !SortedArray.has(childUnit.elements, unit.elements[members[offsets[a[edgeIndex]]]]))
-        )
+        ignore: (edgeIndex: number) => {
+            if (flag[edgeIndex] === InteractionFlag.Filtered) return true;
+
+            if (childUnit) {
+                const f = a[edgeIndex];
+                for (let i = offsets[f], jl = offsets[f + 1]; i < jl; ++i) {
+                    const e = unit.elements[members[offsets[i]]];
+                    if (!SortedArray.has(childUnit.elements, e)) return true;
+                }
+            }
+
+            return false;
+        }
     };
 
     const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
@@ -164,7 +172,6 @@ function eachInteraction(loci: Loci, structureGroup: StructureGroup, apply: (int
             const unitIdx = group.unitIndexMap.get(e.unit.id);
             if (unitIdx === undefined) continue;
 
-            __contactIndicesSet.clear();
             OrderedSet.forEach(e.indices, v => {
                 for (let i = fOffsets[v], il = fOffsets[v + 1]; i < il; ++i) {
                     const fI = fIndices[i];
@@ -188,6 +195,8 @@ function eachInteraction(loci: Loci, structureGroup: StructureGroup, apply: (int
 
                 if (apply(Interval.ofSingleton(unitIdx * groupCount + i))) changed = true;
             });
+
+            __contactIndicesSet.clear();
         }
     }
     return changed;

+ 24 - 2
src/mol-model/structure/model/properties/coarse/hierarchy.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 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>
@@ -63,6 +63,28 @@ export interface CoarseHierarchy {
     gaussians: CoarseElements
 }
 
+const EmptyCoarseElements: CoarseElements = {
+    chainKey: [],
+    entityKey: [],
+    findSequenceKey: () => -1 as ElementIndex,
+    findChainKey: () => -1 as ChainIndex,
+    getEntityFromChain: () => -1 as EntityIndex,
+
+    count: 0,
+    entity_id: Column.Undefined(0, Column.Schema.str),
+    asym_id: Column.Undefined(0, Column.Schema.str),
+    seq_id_begin: Column.Undefined(0, Column.Schema.int),
+    seq_id_end: Column.Undefined(0, Column.Schema.int),
+    chainElementSegments: Segmentation.create([]),
+
+    polymerRanges: SortedRanges.ofSortedRanges([]),
+    gapRanges: SortedRanges.ofSortedRanges([]),
+};
+
 export namespace CoarseHierarchy {
-    export const Empty: CoarseHierarchy = { isDefined: false } as any;
+    export const Empty: CoarseHierarchy = {
+        isDefined: false,
+        spheres: EmptyCoarseElements,
+        gaussians: EmptyCoarseElements
+    };
 }

+ 3 - 3
src/mol-plugin-state/actions/volume.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 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>
@@ -75,8 +75,8 @@ const DownloadDensity = StateAction.build({
         case 'pdb-xray':
             downloadParams = src.params.provider.server === 'pdbe' ? {
                 url: Asset.Url(src.params.type === '2fofc'
-                    ? `http://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}.ccp4`
-                    : `http://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}_diff.ccp4`),
+                    ? `https://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}.ccp4`
+                    : `https://www.ebi.ac.uk/pdbe/coordinates/files/${src.params.provider.id.toLowerCase()}_diff.ccp4`),
                 isBinary: true,
                 label: `PDBe X-ray map: ${src.params.provider.id}`
             } : {

+ 7 - 4
src/mol-repr/structure/visual/util/bond.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -224,6 +224,8 @@ export function getInterBondLoci(pickingId: PickingId, structure: Structure, id:
     return EmptyLoci;
 }
 
+const __unitMap = new Map<number, OrderedSet<StructureElement.UnitIndex>>();
+
 export function eachInterBond(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean, isMarking: boolean) {
     let changed = false;
     if (Bond.isLoci(loci)) {
@@ -238,14 +240,13 @@ export function eachInterBond(loci: Loci, structure: Structure, apply: (interval
         if (!Structure.areEquivalent(loci.structure, structure)) return false;
         if (isMarking && loci.elements.length === 1) return false; // only a single unit
 
-        const map = new Map<number, OrderedSet<StructureElement.UnitIndex>>();
-        for (const e of loci.elements) map.set(e.unit.id, e.indices);
+        for (const e of loci.elements) __unitMap.set(e.unit.id, e.indices);
 
         for (const e of loci.elements) {
             const { unit } = e;
             if (!Unit.isAtomic(unit)) continue;
             structure.interUnitBonds.getConnectedUnits(unit.id).forEach(b => {
-                const otherLociIndices = map.get(b.unitB);
+                const otherLociIndices = __unitMap.get(b.unitB);
                 if (!isMarking || otherLociIndices) {
                     OrderedSet.forEach(e.indices, v => {
                         if (!b.connectedIndices.includes(v)) return;
@@ -259,6 +260,8 @@ export function eachInterBond(loci: Loci, structure: Structure, apply: (interval
                 }
             });
         }
+
+        __unitMap.clear();
     }
     return changed;
 }

+ 16 - 10
src/mol-theme/color/element-symbol.ts

@@ -16,6 +16,9 @@ import { getAdjustedColorMap } from '../../mol-util/color/color';
 import { getColorMapParams } from '../../mol-util/color/params';
 import { ChainIdColorTheme, ChainIdColorThemeParams } from './chain-id';
 import { OperatorNameColorThemeParams, OperatorNameColorTheme } from './operator-name';
+import { EntityIdColorTheme, EntityIdColorThemeParams } from './entity-id';
+import { assertUnreachable } from '../../mol-util/type-helpers';
+import { EntitySourceColorTheme, EntitySourceColorThemeParams } from './entity-source';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF)
 export const ElementSymbolColors = ColorMap({
@@ -26,13 +29,13 @@ export type ElementSymbolColors = typeof ElementSymbolColors
 const DefaultElementSymbolColor = Color(0xFFFFFF);
 const Description = 'Assigns a color to every atom according to its chemical element.';
 
-// TODO generalise `carbonColor` param to all themes?
-
 export const ElementSymbolColorThemeParams = {
     carbonColor: PD.MappedStatic('chain-id', {
-        'chain-id': PD.Group({ ...ChainIdColorThemeParams }),
-        'operator-name': PD.Group({ ...OperatorNameColorThemeParams }),
-        'element-symbol': PD.Group({})
+        'chain-id': PD.Group(ChainIdColorThemeParams),
+        'entity-id': PD.Group(EntityIdColorThemeParams),
+        'entity-source': PD.Group(EntitySourceColorThemeParams),
+        'operator-name': PD.Group(OperatorNameColorThemeParams),
+        'element-symbol': PD.EmptyGroup()
     }, { description: 'Use chain-id coloring for carbon atoms.' }),
     saturation: PD.Numeric(0, { min: -6, max: 6, step: 0.1 }),
     lightness: PD.Numeric(0.2, { min: -6, max: 6, step: 0.1 }),
@@ -54,11 +57,14 @@ export function elementSymbolColor(colorMap: ElementSymbolColors, element: Eleme
 export function ElementSymbolColorTheme(ctx: ThemeDataContext, props: PD.Values<ElementSymbolColorThemeParams>): ColorTheme<ElementSymbolColorThemeParams> {
     const colorMap = getAdjustedColorMap(props.colors.name === 'default' ? ElementSymbolColors : props.colors.params, props.saturation, props.lightness);
 
-    const carbonColor = props.carbonColor.name === 'chain-id'
-        ? ChainIdColorTheme(ctx, props.carbonColor.params).color
-        : props.carbonColor.name === 'operator-name'
-            ? OperatorNameColorTheme(ctx, props.carbonColor.params).color
-            : undefined;
+    const pcc = props.carbonColor;
+    const carbonColor =
+        pcc.name === 'chain-id' ? ChainIdColorTheme(ctx, pcc.params).color :
+            pcc.name === 'entity-id' ? EntityIdColorTheme(ctx, pcc.params).color :
+                pcc.name === 'entity-source' ? EntitySourceColorTheme(ctx, pcc.params).color :
+                    pcc.name === 'operator-name' ? OperatorNameColorTheme(ctx, pcc.params).color :
+                        pcc.name === 'element-symbol' ? undefined :
+                            assertUnreachable(pcc);
 
     function elementColor(element: ElementSymbol, location: Location) {
         return (carbonColor && element === 'C')

+ 12 - 9
src/mol-theme/color/entity-id.ts

@@ -38,15 +38,18 @@ function getEntityIdSerialMap(structure: Structure) {
             const k = key(label_entity_id.value(j), i);
             if (!map.has(k)) map.set(k, map.size);
         }
-        const { entity_id: spheres_entity_id } = structure.models[i].coarseHierarchy.spheres;
-        for (let j = 0, jl = spheres_entity_id.rowCount; j < jl; ++j) {
-            const k = key(spheres_entity_id.value(j), i);
-            if (!map.has(k)) map.set(k, map.size);
-        }
-        const { entity_id: gaussians_entity_id } = structure.models[i].coarseHierarchy.gaussians;
-        for (let j = 0, jl = gaussians_entity_id.rowCount; j < jl; ++j) {
-            const k = key(gaussians_entity_id.value(j), i);
-            if (!map.has(k)) map.set(k, map.size);
+        const { coarseHierarchy } = structure.models[i];
+        if (coarseHierarchy.isDefined) {
+            const { entity_id: spheres_entity_id } = coarseHierarchy.spheres;
+            for (let j = 0, jl = spheres_entity_id.rowCount; j < jl; ++j) {
+                const k = key(spheres_entity_id.value(j), i);
+                if (!map.has(k)) map.set(k, map.size);
+            }
+            const { entity_id: gaussians_entity_id } = coarseHierarchy.gaussians;
+            for (let j = 0, jl = gaussians_entity_id.rowCount; j < jl; ++j) {
+                const k = key(gaussians_entity_id.value(j), i);
+                if (!map.has(k)) map.set(k, map.size);
+            }
         }
     }
     return map;