mirror of
https://github.com/molstar/molstar.git
synced 2026-06-05 22:31:26 +08:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f75861de | ||
|
|
ccd04dbc9d | ||
|
|
e71f8d2c10 | ||
|
|
d9b4c60239 | ||
|
|
103c1fca21 | ||
|
|
49559bf5fb | ||
|
|
26dceabf83 | ||
|
|
abe506182e | ||
|
|
582a0e2a38 | ||
|
|
2784ccf379 | ||
|
|
0ad1d578fe | ||
|
|
31fd1c9c68 | ||
|
|
9c961297a2 | ||
|
|
b920053349 | ||
|
|
0a5c764e4a | ||
|
|
b9a71c83ff | ||
|
|
3255f207d0 | ||
|
|
e3b4ca8862 | ||
|
|
6810793015 | ||
|
|
1feb3c2095 | ||
|
|
f2da6033d0 | ||
|
|
28bc212132 | ||
|
|
1a7c62eec6 | ||
|
|
de67dbacba | ||
|
|
57223a0f9a |
19
CHANGELOG.md
19
CHANGELOG.md
@@ -6,6 +6,25 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v3.0.2] - 2022-01-30
|
||||
|
||||
- Fix color smoothing of elongated structures (by fixing ``Sphere.expand`` for spheres with highly directional extrema)
|
||||
- Fix entity label not displayed when multiple instances of the same entity are highlighted
|
||||
- Fix empty elements created in ``StructureElement.Loci.extendToAllInstances``
|
||||
- Measurement options tweaks (allow larger ``textSize``; make ``customText`` essential)
|
||||
- Fix visual visibility sync edge case when changing state snapshots
|
||||
|
||||
## [v3.0.1] - 2022-01-27
|
||||
|
||||
- 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``
|
||||
- Emit drag event whenever started within viewport (not only for non-empty loci)
|
||||
|
||||
## [v3.0.0] - 2022-01-23
|
||||
|
||||
- Assembly handling tweaks:
|
||||
|
||||
72
CITATION.cff
Normal file
72
CITATION.cff
Normal file
@@ -0,0 +1,72 @@
|
||||
cff-version: 1.2.0
|
||||
title: >-
|
||||
Mol* library
|
||||
message: >-
|
||||
Please cite this software using the metadata from
|
||||
'preferred-citation'.
|
||||
authors:
|
||||
- given-names: Alexander S
|
||||
family-names: Rose
|
||||
orcid: 'https://orcid.org/0000-0002-0893-5551'
|
||||
- given-names: David
|
||||
family-names: Sehnal
|
||||
orcid: 'https://orcid.org/0000-0002-0682-3089'
|
||||
- given-names: Sebastian
|
||||
family-names: Bittrich
|
||||
orcid: 'https://orcid.org/0000-0003-3576-0387'
|
||||
- given-names: Áron Samuel
|
||||
family-names: Kovács
|
||||
- given-names: Ludovic
|
||||
family-names: Autin
|
||||
orcid: 'https://orcid.org/0000-0002-2197-191X'
|
||||
- given-names: Michal
|
||||
family-names: Malý
|
||||
- given-names: Jiří
|
||||
family-names: Černý
|
||||
- given-names: Panagiotis
|
||||
family-names: Tourlas
|
||||
type: software
|
||||
doi: 10.5281/zenodo.3947306
|
||||
preferred-citation:
|
||||
authors:
|
||||
- given-names: David
|
||||
family-names: Sehnal
|
||||
orcid: 'https://orcid.org/0000-0002-0682-3089'
|
||||
- given-names: Sebastian
|
||||
family-names: Bittrich
|
||||
orcid: 'https://orcid.org/0000-0003-3576-0387'
|
||||
- given-names: Mandar
|
||||
family-names: Deshpande
|
||||
orcid: 'https://orcid.org/0000-0002-9043-7665'
|
||||
- given-names: Radka
|
||||
family-names: Svobodová
|
||||
orcid: 'https://orcid.org/0000-0002-3840-8760'
|
||||
- given-names: Karel
|
||||
family-names: Berka
|
||||
orcid: 'https://orcid.org/0000-0001-9472-2589'
|
||||
- given-names: Václav
|
||||
family-names: Bazgier
|
||||
orcid: 'https://orcid.org/0000-0003-3393-3010'
|
||||
- given-names: Sameer
|
||||
family-names: Velankar
|
||||
orcid: 'https://orcid.org/0000-0002-8439-5964'
|
||||
- given-names: Stephen K
|
||||
family-names: Burley
|
||||
orcid: 'https://orcid.org/0000-0002-2487-9713'
|
||||
- given-names: Jaroslav
|
||||
family-names: Koča
|
||||
orcid: 'https://orcid.org/0000-0002-2780-4901'
|
||||
- given-names: Alexander S
|
||||
family-names: Rose
|
||||
orcid: 'https://orcid.org/0000-0002-0893-5551'
|
||||
title: >-
|
||||
Mol* Viewer: modern web app for 3D visualization
|
||||
and analysis of large biomolecular structures
|
||||
type: article
|
||||
doi: 10.1093/nar/gkab314
|
||||
journal: "Nucleic Acids Research"
|
||||
issue: W1
|
||||
volume: 49
|
||||
year: 2021
|
||||
month: 7
|
||||
pages: "W431–W437"
|
||||
4270
package-lock.json
generated
4270
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.2",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -13,7 +13,7 @@
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"lint-fix": "eslint . --fix",
|
||||
"test": "npm run lint && jest",
|
||||
"test": "npm install --no-save \"gl@^5.0.0\" && npm run lint && jest",
|
||||
"jest": "jest",
|
||||
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
|
||||
"clean": "node ./scripts/clean.js",
|
||||
@@ -156,8 +156,5 @@
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"gl": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ModelExport } from '../../extensions/model-export';
|
||||
import { Mp4Export } from '../../extensions/mp4-export';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
|
||||
import { Volume } from '../../mol-model/volume';
|
||||
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
|
||||
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
|
||||
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
|
||||
@@ -366,11 +367,13 @@ export class Viewer {
|
||||
|
||||
const repr = plugin.build();
|
||||
for (const iso of isovalues) {
|
||||
const volume: StateObjectSelector<PluginStateObject.Volume.Data> = parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume;
|
||||
const volumeData = volume.cell!.obj!.data;
|
||||
repr
|
||||
.to(parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume)
|
||||
.to(volume)
|
||||
.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, firstVolume.data!, {
|
||||
type: 'isosurface',
|
||||
typeParams: { alpha: iso.alpha ?? 1, isoValue: iso.type === 'absolute' ? { kind: 'absolute', absoluteValue: iso.value } : { kind: 'relative', relativeValue: iso.value } },
|
||||
typeParams: { alpha: iso.alpha ?? 1, isoValue: Volume.adjustedIsoValue(volumeData, iso.value, iso.type) },
|
||||
color: 'uniform',
|
||||
colorParams: { value: iso.color }
|
||||
}));
|
||||
|
||||
@@ -71,7 +71,7 @@ export class Canvas3dInteractionHelper {
|
||||
const xyChanged = this.startX !== this.endX || this.startY !== this.endY;
|
||||
|
||||
if (e === InputEvent.Drag) {
|
||||
if (xyChanged && !Representation.Loci.isEmpty(this.prevLoci)) {
|
||||
if (xyChanged && !this.outsideViewport(this.startX, this.startY)) {
|
||||
this.events.drag.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, pageStart: Vec2.create(this.startX, this.startY), pageEnd: Vec2.create(this.endX, this.endY) });
|
||||
|
||||
this.startX = this.endX;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
@@ -11,6 +11,7 @@ import { OrderedSet } from '../../../mol-data/int';
|
||||
import { NumberArray, PickRequired } from '../../../mol-util/type-helpers';
|
||||
import { Box3D } from './box3d';
|
||||
import { Axes3D } from './axes3d';
|
||||
import { PrincipalAxes } from '../../linear-algebra/matrix/principal-axes';
|
||||
|
||||
interface Sphere3D {
|
||||
center: Vec3,
|
||||
@@ -202,11 +203,28 @@ namespace Sphere3D {
|
||||
return out;
|
||||
}
|
||||
if (hasExtrema(sphere)) {
|
||||
const positions = new Float32Array(sphere.extrema.length * 3);
|
||||
for (let i = 0; i < sphere.extrema.length; i++) {
|
||||
Vec3.toArray(sphere.extrema[i], positions, i * 3);
|
||||
}
|
||||
|
||||
const axes = PrincipalAxes.calculateMomentsAxes(positions);
|
||||
Axes3D.scale(axes, Axes3D.normalize(axes, axes), delta);
|
||||
|
||||
setExtrema(out, sphere.extrema.map(e => {
|
||||
Vec3.sub(tmpDir, e, sphere.center);
|
||||
const dist = Vec3.distance(sphere.center, e);
|
||||
Vec3.normalize(tmpDir, tmpDir);
|
||||
return Vec3.scaleAndAdd(Vec3(), sphere.center, tmpDir, dist + delta);
|
||||
const out = Vec3.clone(e);
|
||||
|
||||
const sA = Vec3.dot(tmpDir, axes.dirA) < 0 ? -1 : 1;
|
||||
Vec3.scaleAndAdd(out, out, axes.dirA, sA);
|
||||
|
||||
const sB = Vec3.dot(tmpDir, axes.dirB) < 0 ? -1 : 1;
|
||||
Vec3.scaleAndAdd(out, out, axes.dirB, sB);
|
||||
|
||||
const sC = Vec3.dot(tmpDir, axes.dirC) < 0 ? -1 : 1;
|
||||
Vec3.scaleAndAdd(out, out, axes.dirC, sC);
|
||||
|
||||
return out;
|
||||
}));
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2017-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>
|
||||
@@ -497,7 +497,9 @@ export namespace Loci {
|
||||
if (!elementIndices) continue;
|
||||
|
||||
const indices = getUnitIndices(unit.elements, elementIndices);
|
||||
elements[elements.length] = { unit, indices };
|
||||
if (OrderedSet.size(indices)) {
|
||||
elements[elements.length] = { unit, indices };
|
||||
}
|
||||
}
|
||||
|
||||
return Loci(loci.structure, elements);
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ModelFormat } from '../../mol-model-formats/format';
|
||||
import { CustomProperties } from '../custom-property';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { toPrecision } from '../../mol-util/number';
|
||||
import { DscifFormat } from '../../mol-model-formats/volume/density-server';
|
||||
|
||||
export interface Volume {
|
||||
readonly label?: string
|
||||
@@ -84,6 +85,23 @@ export namespace Volume {
|
||||
}
|
||||
}
|
||||
|
||||
// Converts iso value to relative if using downsample VolumeServer data
|
||||
export function adjustedIsoValue(volume: Volume, value: number, kind: 'absolute' | 'relative') {
|
||||
if (kind === 'relative') return IsoValue.relative(value);
|
||||
|
||||
const absolute = IsoValue.absolute(value);
|
||||
if (DscifFormat.is(volume.sourceData)) {
|
||||
const stats = {
|
||||
min: volume.sourceData.data.volume_data_3d_info.min_source.value(0),
|
||||
max: volume.sourceData.data.volume_data_3d_info.max_source.value(0),
|
||||
mean: volume.sourceData.data.volume_data_3d_info.mean_source.value(0),
|
||||
sigma: volume.sourceData.data.volume_data_3d_info.sigma_source.value(0),
|
||||
};
|
||||
return Volume.IsoValue.toRelative(absolute, stats);
|
||||
}
|
||||
return absolute;
|
||||
}
|
||||
|
||||
const defaultStats: Grid['stats'] = { min: -1, max: 1, mean: 0, sigma: 0.1 };
|
||||
export function createIsoValueParam(defaultValue: Volume.IsoValue, stats?: Grid['stats']) {
|
||||
const sts = stats || defaultStats;
|
||||
|
||||
@@ -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}`
|
||||
} : {
|
||||
|
||||
@@ -18,7 +18,6 @@ import { objectForEach } from '../../mol-util/object';
|
||||
import { RecommendedIsoValue } from '../../mol-model-formats/volume/property';
|
||||
import { getContourLevelEmdb } from '../../mol-plugin/behavior/dynamic/volume-streaming/util';
|
||||
import { Task } from '../../mol-task';
|
||||
import { DscifFormat } from '../../mol-model-formats/volume/density-server';
|
||||
|
||||
export const VolumeFormatCategory = 'Volume';
|
||||
type Params = { entryId?: string };
|
||||
@@ -42,19 +41,9 @@ async function tryObtainRecommendedIsoValue(plugin: PluginContext, volume?: Volu
|
||||
function tryGetRecomendedIsoValue(volume: Volume) {
|
||||
const recommendedIsoValue = RecommendedIsoValue.Provider.get(volume);
|
||||
if (!recommendedIsoValue) return;
|
||||
|
||||
if (recommendedIsoValue.kind === 'relative') return recommendedIsoValue;
|
||||
|
||||
let stats = volume.grid.stats;
|
||||
if (DscifFormat.is(volume.sourceData)) {
|
||||
stats = {
|
||||
min: volume.sourceData.data.volume_data_3d_info.min_source.value(0),
|
||||
max: volume.sourceData.data.volume_data_3d_info.max_source.value(0),
|
||||
mean: volume.sourceData.data.volume_data_3d_info.mean_source.value(0),
|
||||
sigma: volume.sourceData.data.volume_data_3d_info.sigma_source.value(0),
|
||||
};
|
||||
}
|
||||
return Volume.IsoValue.toRelative(recommendedIsoValue, stats);
|
||||
return Volume.adjustedIsoValue(volume, recommendedIsoValue.absoluteValue, 'absolute');
|
||||
}
|
||||
|
||||
async function defaultVisuals(plugin: PluginContext, data: { volume: StateObjectSelector<PluginStateObject.Volume.Data> }) {
|
||||
|
||||
@@ -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 David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -212,11 +212,14 @@ export const DefaultLociLabelProvider = PluginBehavior.create({
|
||||
private f = {
|
||||
label: (loci: Loci) => {
|
||||
const label: string[] = [];
|
||||
if (StructureElement.Loci.is(loci) && loci.elements.length === 1) {
|
||||
const { unit: u } = loci.elements[0];
|
||||
const l = StructureElement.Location.create(loci.structure, u, u.elements[0]);
|
||||
const name = StructureProperties.entity.pdbx_description(l).join(', ');
|
||||
label.push(name);
|
||||
if (StructureElement.Loci.is(loci)) {
|
||||
const entityNames = new Set<string>();
|
||||
for (const { unit: u } of loci.elements) {
|
||||
const l = StructureElement.Location.create(loci.structure, u, u.elements[0]);
|
||||
const name = StructureProperties.entity.pdbx_description(l).join(', ');
|
||||
entityNames.add(name);
|
||||
}
|
||||
if (entityNames.size === 1) entityNames.forEach(name => label.push(name));
|
||||
}
|
||||
label.push(lociLabel(loci));
|
||||
return label.filter(l => !!l).join('</br>');
|
||||
|
||||
@@ -84,7 +84,7 @@ export function UpdateRepresentationVisibility(ctx: PluginContext) {
|
||||
}
|
||||
|
||||
function updateVisibility(cell: StateObjectCell, r: Representation<any>) {
|
||||
if (r.state.visible === cell.state.isHidden) {
|
||||
if (r.state.visible === !!cell.state.isHidden) {
|
||||
r.setState({ visible: !cell.state.isHidden });
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -10,9 +10,9 @@ import { ColorNames } from '../../../mol-util/color/names';
|
||||
import { Text } from '../../../mol-geo/geometry/text/text';
|
||||
|
||||
export const MeasurementRepresentationCommonTextParams = {
|
||||
customText: PD.Text('', { label: 'Text', description: 'Override the label with custom value.' }),
|
||||
customText: PD.Text('', { label: 'Text', description: 'Override the label with custom value.', isEssential: true }),
|
||||
textColor: PD.Color(ColorNames.black, { isEssential: true }),
|
||||
textSize: PD.Numeric(0.5, { min: 0.1, max: 5, step: 0.1 }, { isEssential: true }),
|
||||
textSize: PD.Numeric(0.5, { min: 0.1, max: 10, step: 0.1 }, { isEssential: true }),
|
||||
};
|
||||
|
||||
export const LociLabelTextParams = {
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -57,7 +57,7 @@ async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, struct
|
||||
Mesh.transform(surface, transform);
|
||||
if (ctx.webgl && !ctx.webgl.isWebGL2) Mesh.uniformTriangleGroup(surface);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, props.probeRadius + getUnitExtraRadius(unit));
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, getUnitExtraRadius(unit));
|
||||
surface.setBoundingSphere(sphere);
|
||||
(surface.meta as MolecularSurfaceMeta).resolution = resolution;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -76,14 +76,23 @@ namespace Transform {
|
||||
const s = (b as any)[k], t = (a as any)[k];
|
||||
if (!!s === !!t) continue;
|
||||
changed = true;
|
||||
(a as any)[k] = s;
|
||||
if (s !== void 0) {
|
||||
(a as any)[k] = s;
|
||||
} else {
|
||||
delete (a as any)[k];
|
||||
}
|
||||
}
|
||||
for (const k of Object.keys(a)) {
|
||||
const s = (b as any)[k], t = (a as any)[k];
|
||||
if (!!s === !!t) continue;
|
||||
changed = true;
|
||||
(a as any)[k] = s;
|
||||
if (s !== void 0) {
|
||||
(a as any)[k] = s;
|
||||
} else {
|
||||
delete (a as any)[k];
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -197,13 +197,13 @@ export function configureLocal() {
|
||||
description: VOLUME_SERVER_HEADER
|
||||
});
|
||||
parser.add_argument('--jobs', { help: `Path to a JSON file with job specification.`, required: false });
|
||||
parser.add_argument('--jobsTemplate', { help: 'Print example template for jobs.json and exit.', required: false, nargs: 0 });
|
||||
parser.add_argument('--jobsTemplate', { help: 'Print example template for jobs.json and exit.', required: false, action: 'store_true' });
|
||||
addJsonConfigArgs(parser);
|
||||
addLimitsArgs(parser);
|
||||
|
||||
const config = parser.parse_args() as LimitsConfig & ServerJsonConfig;
|
||||
|
||||
if (config.cfgTemplate !== null) {
|
||||
if (config.cfgTemplate) {
|
||||
console.log(JSON.stringify(DefaultLimitsConfig, null, 2));
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -216,7 +216,7 @@ export function configureLocal() {
|
||||
setLimitsConfig(cfg);
|
||||
}
|
||||
|
||||
if (config.printCfg !== null) {
|
||||
if (config.printCfg) {
|
||||
console.log(JSON.stringify(LimitsConfig, null, 2));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import * as LocalApi from './server/local-api';
|
||||
|
||||
const config = configureLocal();
|
||||
|
||||
if (config.jobsTemplate !== null) {
|
||||
if (config.jobsTemplate) {
|
||||
const exampleJobs: LocalApi.JobEntry[] = [{
|
||||
source: {
|
||||
filename: `g:/test/mdb/xray-1tqn.mdb`,
|
||||
|
||||
Reference in New Issue
Block a user