Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Rose
1db0ada684 debugging 2022-08-20 16:50:39 -07:00
105 changed files with 1667 additions and 7497 deletions

View File

@@ -6,28 +6,6 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
- [Fix] Clone ``Canvas3DParams`` when creating a ``Canvas3D`` instance to prevent shared state between multiple instances
- Add ``includeResidueTest`` option to ``alignAndSuperposeWithSIFTSMapping``
- Add ``parentDisplay`` param for interactions representation.
- [Experimental] Add support for PyMOL, VMD, and Jmol atom expressions in selection scripts
- Support for ``failIfMajorPerformanceCaveat`` webgl attribute. Add ``PluginConfig.General.AllowMajorPerformanceCaveat`` and ``allow-major-performance-caveat`` Viewer GET param.
- Fix handling of PDB TER records (#549)
- Add support for getting multiple loci from a representation (``.getAllLoci()``)
- Add ``key`` property to intra- and inter-bonds for referencing source data
- Fix click event triggered after move
## [v3.16.0] - 2022-08-25
- Support ``globalColorParams`` and ``globalSymmetryParams`` in common representation params
- Support ``label`` parameter in ``Viewer.loadStructureFromUrl``
- Fix ``ViewportHelpContent`` Mouse Controls section
## [v3.15.0] - 2022-08-23
- Fix wboit in Safari >=15 (add missing depth renderbuffer to wboit pass)
- Add 'Around Camera' option to Volume streaming
- Avoid queuing more than one update in Volume streaming
## [v3.14.0] - 2022-08-20
- Expose inter-bonds compute params in structure

3009
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.17.0",
"version": "3.14.0",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -89,59 +89,57 @@
"Ludovic Autin <autin@scripps.edu>",
"Michal Malý <michal.maly@ibt.cas.cz>",
"Jiří Černý <jiri.cerny@ibt.cas.cz>",
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>",
"Adam Midlik <midlik@gmail.com>",
"Koya Sakuma <koya.sakuma.work@gmail.com>"
"Panagiotis Tourlas <panagiot_tourlov@hotmail.com>"
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^3.2.1",
"@graphql-codegen/cli": "^2.12.0",
"@graphql-codegen/cli": "^2.11.6",
"@graphql-codegen/time": "^3.2.1",
"@graphql-codegen/typescript": "^2.7.3",
"@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
"@graphql-codegen/typescript-graphql-request": "^4.5.4",
"@graphql-codegen/typescript-graphql-request": "^4.5.3",
"@graphql-codegen/typescript-operations": "^2.5.3",
"@types/cors": "^2.8.12",
"@types/gl": "^4.1.1",
"@types/jest": "^29.0.1",
"@types/react": "^18.0.19",
"@types/jest": "^28.1.7",
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"@typescript-eslint/eslint-plugin": "^5.33.1",
"@typescript-eslint/parser": "^5.33.1",
"benchmark": "^2.1.4",
"concurrently": "^7.4.0",
"concurrently": "^7.3.0",
"cpx2": "^4.2.0",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.7.1",
"eslint": "^8.23.0",
"eslint": "^8.22.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.2.0",
"fs-extra": "^10.1.0",
"graphql": "^16.6.0",
"http-server": "^14.1.1",
"jest": "^29.0.3",
"jest": "^28.1.3",
"mini-css-extract-plugin": "^2.6.1",
"path-browserify": "^1.0.1",
"raw-loader": "^4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.54.9",
"sass": "^1.54.5",
"sass-loader": "^13.0.2",
"simple-git": "^3.14.0",
"simple-git": "^3.12.0",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"ts-jest": "^29.0.0",
"typescript": "^4.8.3",
"ts-jest": "^28.0.8",
"typescript": "^4.7.4",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
},
"dependencies": {
"@types/argparse": "^2.0.10",
"@types/benchmark": "^2.1.2",
"@types/benchmark": "^2.1.1",
"@types/compression": "1.7.2",
"@types/express": "^4.17.13",
"@types/node": "^16.11.58",
"@types/node": "^16.11.51",
"@types/node-fetch": "^2.6.2",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",

View File

@@ -166,7 +166,7 @@ class Viewer {
structures.push({ ref: structureProperties?.ref || structure.ref });
}
// remove current structures from hierarchy as they will be merged
// remove current structuresfrom hierarchy as they will be merged
// TODO only works with using loadStructuresFromUrlsAndMerge once
// need some more API metho to work with the hierarchy
this.plugin.managers.structure.hierarchy.updateCurrent(this.plugin.managers.structure.hierarchy.current.structures, 'remove');

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -202,14 +202,14 @@ const InteractionsPreset = StructureRepresentationPresetProvider({
const components = {
ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
surroundings: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandSurroundings, `surroundings`),
interactions: await presetStaticComponent(plugin, structureCell, 'ligand'),
interactions: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandPlusSurroundings, `interactions`)
};
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
const representations = {
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }),
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial, includeParent: true, parentDisplay: 'between' }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, material: CustomMaterial, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
};

View File

@@ -89,7 +89,6 @@ const DefaultViewerOptions = {
pickPadding: PluginConfig.General.PickPadding.defaultValue,
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
preferWebgl1: PluginConfig.General.PreferWebGl1.defaultValue,
allowMajorPerformanceCaveat: PluginConfig.General.AllowMajorPerformanceCaveat.defaultValue,
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
@@ -160,7 +159,6 @@ export class Viewer {
[PluginConfig.General.PickPadding, o.pickPadding],
[PluginConfig.General.EnableWboit, o.enableWboit],
[PluginConfig.General.PreferWebGl1, o.preferWebgl1],
[PluginConfig.General.AllowMajorPerformanceCaveat, o.allowMajorPerformanceCaveat],
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
[PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
@@ -201,7 +199,7 @@ export class Viewer {
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions & { label?: string }) {
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
@@ -210,7 +208,6 @@ export class Viewer {
url: Asset.Url(url),
format: format as any,
isBinary,
label: options?.label,
options: { ...params.source.params.options, representationParams: options?.representationParams as any },
}
}
@@ -499,4 +496,4 @@ export const ViewerAutoPreset = StructureRepresentationPresetProvider({
return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
}
}
});
});

View File

@@ -38,15 +38,6 @@
viewer.loadPdb('7bv2');
viewer.loadEmdb('EMD-30210', { detail: 6 });
// viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
// viewer.loadStructureFromUrl('my url', 'pdb', false, {
// representationParams: {
// theme: {
// globalName: 'uniform',
// globalColorParams: { value: 0xff0000 }
// }
// },
// label: 'my structure'
// });
});
</script>
</body>

View File

@@ -61,7 +61,6 @@
var pickPadding = getParam('pick-padding', '[^&]+').trim();
var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
var allowMajorPerformanceCaveat = getParam('allow-major-performance-caveat', '[^&]+').trim() === '1';
molstar.Viewer.create('app', {
layoutShowControls: !hideControls,
@@ -77,7 +76,6 @@
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
enableWboit: disableWboit ? false : void 0, // use default value if disable-wboit is not set
preferWebgl1: preferWebgl1,
allowMajorPerformanceCaveat: allowMajorPerformanceCaveat,
}).then(viewer => {
var snapshotId = getParam('snapshot-id', '[^&]+').trim();
if (snapshotId) viewer.setRemoteSnapshot(snapshotId);

View File

@@ -60,8 +60,6 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
}
componentDidMount() {
if (!this.plugin.canvas3d) return;
const merged = merge(
this.controls.behaviors.params,
this.plugin.canvas3d!.reprCount

View File

@@ -118,13 +118,11 @@ export class Mp4Controls extends PluginComponent {
}
private init() {
if (!this.plugin.canvas3d) return;
this.subscribe(this.plugin.managers.animation.events.updated.pipe(debounceTime(16)), () => {
this.sync();
});
this.subscribe(this.plugin.canvas3d.resized, () => this.syncInfo());
this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));

View File

@@ -42,7 +42,6 @@ import { MarkingParams } from './passes/marking';
import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit } from '../mol-gl/webgl/render-item';
import { degToRad, radToDeg } from '../mol-math/misc';
import { AssetManager } from '../mol-util/assets';
import { deepClone } from '../mol-util/object';
export const Canvas3DParams = {
camera: PD.Group({
@@ -117,7 +116,6 @@ interface Canvas3DContext {
namespace Canvas3DContext {
export const DefaultAttribs = {
failIfMajorPerformanceCaveat: false,
/** true by default to avoid issues with Safari (Jan 2021) */
antialias: true,
/** true to support multiple Canvas3D objects with a single context */
@@ -133,9 +131,8 @@ namespace Canvas3DContext {
export function fromCanvas(canvas: HTMLCanvasElement, assetManager: AssetManager, attribs: Partial<Attribs> = {}): Canvas3DContext {
const a = { ...DefaultAttribs, ...attribs };
const { failIfMajorPerformanceCaveat, antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
const { antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
const gl = getGLContext(canvas, {
failIfMajorPerformanceCaveat,
antialias,
preserveDrawingBuffer,
alpha: true, // the renderer requires an alpha channel
@@ -288,7 +285,7 @@ namespace Canvas3D {
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
export function create({ webgl, input, passes, attribs, assetManager }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
const p: Canvas3DProps = { ...deepClone(DefaultCanvas3DParams), ...deepClone(props) };
const p: Canvas3DProps = { ...DefaultCanvas3DParams, ...props };
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>();

View File

@@ -18,8 +18,6 @@ import { evaluateWboit_frag } from '../../mol-gl/shader/evaluate-wboit.frag';
import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
import { Vec2 } from '../../mol-math/linear-algebra';
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
import { isWebGL2 } from '../../mol-gl/webgl/compat';
import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
const EvaluateWboitSchema = {
...QuadSchema,
@@ -52,7 +50,6 @@ export class WboitPass {
private readonly framebuffer: Framebuffer;
private readonly textureA: Texture;
private readonly textureB: Texture;
private readonly depthRenderbuffer: Renderbuffer;
private _supported = false;
get supported() {
@@ -90,7 +87,6 @@ export class WboitPass {
if (width !== w || height !== h) {
this.textureA.define(width, height);
this.textureB.define(width, height);
this.depthRenderbuffer.setSize(width, height);
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
}
}
@@ -110,8 +106,6 @@ export class WboitPass {
this.textureA.attachFramebuffer(this.framebuffer, 'color0');
this.textureB.attachFramebuffer(this.framebuffer, 'color1');
this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
}
static isSupported(webgl: WebGLContext) {
@@ -134,7 +128,7 @@ export class WboitPass {
constructor(private webgl: WebGLContext, width: number, height: number) {
if (!WboitPass.isSupported(webgl)) return;
const { resources, gl } = webgl;
const { resources } = webgl;
this.textureA = resources.texture('image-float32', 'rgba', 'float', 'nearest');
this.textureA.define(width, height);
@@ -142,10 +136,6 @@ export class WboitPass {
this.textureB = resources.texture('image-float32', 'rgba', 'float', 'nearest');
this.textureB.define(width, height);
this.depthRenderbuffer = isWebGL2(gl)
? resources.renderbuffer('depth32f', 'depth', width, height)
: resources.renderbuffer('depth16', 'depth', width, height);
this.renderable = getEvaluateWboitRenderable(webgl, this.textureA, this.textureB);
this.framebuffer = resources.framebuffer();

View File

@@ -44,9 +44,10 @@ varying vec3 vModelPosition;
varying vec3 vViewPosition;
#if defined(noNonInstancedActiveAttribs)
// int() is needed for some Safari versions
// see https://bugs.webkit.org/show_bug.cgi?id=244152
#define VertexID int(gl_VertexID)
#define VertexID gl_VertexID // for testing
// // int() is needed for some Safari versions
// // see https://bugs.webkit.org/show_bug.cgi?id=244152
// #define VertexID int(gl_VertexID)
#else
attribute float aVertex;
#define VertexID int(aVertex)

View File

@@ -109,14 +109,14 @@ void main() {
vec3 vViewPosition = vModelPosition + intersection.x * rayDir;
vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz;
float fragmentDepth = calcDepth(vViewPosition);
if (fragmentDepth < 0.0) discard;
if (fragmentDepth > 1.0) discard;
gl_FragDepthEXT = fragmentDepth;
gl_FragDepthEXT = calcDepth(vViewPosition);
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
if (gl_FragDepthEXT < 0.0) discard;
if (gl_FragDepthEXT > 1.0) discard;
float fragmentDepth = gl_FragDepthEXT;
#include assign_material_color
#if defined(dRenderVariant_pick)

View File

@@ -70,17 +70,17 @@ void main(void){
}
vec3 vViewPosition = cameraPos;
float fragmentDepth = calcDepth(vViewPosition);
if (!flag && fragmentDepth >= 0.0) {
fragmentDepth = 0.0 + (0.0000001 / vRadius);
gl_FragDepthEXT = calcDepth(vViewPosition);
if (!flag && gl_FragDepthEXT >= 0.0) {
gl_FragDepthEXT = 0.0 + (0.0000001 / vRadius);
}
if (fragmentDepth < 0.0) discard;
if (fragmentDepth > 1.0) discard;
gl_FragDepthEXT = fragmentDepth;
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
if (gl_FragDepthEXT < 0.0) discard;
if (gl_FragDepthEXT > 1.0) discard;
float fragmentDepth = gl_FragDepthEXT;
#include assign_material_color
#if defined(dRenderVariant_pick)

View File

@@ -35,7 +35,7 @@ function Vec3() {
namespace Vec3 {
export function zero(): Vec3 {
const out = [0.1, 0.0, 0.0]; // ensure backing array of type double
const out = [0.1, 0.0, 0.0];
out[0] = 0;
return out as any;
}

View File

@@ -32,7 +32,6 @@ const DnaAtomIdsList = [
/** Used to reduce false positives for atom name-based type guessing */
const NonPolymerNames = new Set([
'FMN', 'NCN', 'FNS', 'FMA', 'ATP', 'ADP', 'AMP', 'GTP', 'GDP', 'GMP', // Mononucleotides
'LIG'
]);
const StandardComponents = (function () {

View File

@@ -39,7 +39,7 @@ export function getAtomSiteTemplate(data: string, count: number) {
};
}
export function getAtomSite(sites: AtomSiteTemplate, terIndices: Set<number>): { [K in keyof mmCIF_Schema['atom_site'] | 'partial_charge']?: CifField } {
export function getAtomSite(sites: AtomSiteTemplate, hasTer: boolean): { [K in keyof mmCIF_Schema['atom_site'] | 'partial_charge']?: CifField } {
const pdbx_PDB_model_num = CifField.ofStrings(sites.pdbx_PDB_model_num);
const auth_asym_id = CifField.ofTokens(sites.auth_asym_id);
const auth_seq_id = CifField.ofTokens(sites.auth_seq_id);
@@ -67,17 +67,21 @@ export function getAtomSite(sites: AtomSiteTemplate, terIndices: Set<number>): {
const seqId = auth_seq_id.int(i);
let atomId = auth_atom_id.str(i);
let asymIdChanged = false;
if (modelNum !== currModelNum) {
asymIdCounts.clear();
atomIdCounts.clear();
currModelNum = modelNum;
currAsymId = asymId;
currSeqId = seqId;
asymIdChanged = true;
currLabelAsymId = asymId;
} else if (currAsymId !== asymId) {
atomIdCounts.clear();
currAsymId = asymId;
currSeqId = seqId;
asymIdChanged = true;
currLabelAsymId = asymId;
} else if (currSeqId !== seqId) {
atomIdCounts.clear();
@@ -87,7 +91,7 @@ export function getAtomSite(sites: AtomSiteTemplate, terIndices: Set<number>): {
if (asymIdCounts.has(asymId)) {
// only change the chains name if there are TER records
// otherwise assume repeated chain name use is from interleaved chains
if (terIndices.has(i)) {
if (hasTer && asymIdChanged) {
const asymIdCount = asymIdCounts.get(asymId)! + 1;
asymIdCounts.set(asymId, asymIdCount);
currLabelAsymId = `${asymId}_${asymIdCount}`;

View File

@@ -51,7 +51,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
let modelNum = 0, modelStr = '';
let conectRange: [number, number] | undefined = undefined;
const terIndices = new Set<number>();
let hasTer = false;
for (let i = 0, _i = lines.count; i < _i; i++) {
let s = indices[2 * i], e = indices[2 * i + 1];
@@ -164,7 +164,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
break;
case 'T':
if (substringStartsWith(data, s, e, 'TER')) {
terIndices.add(atomSite.index);
hasTer = true;
}
}
}
@@ -183,7 +183,7 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
atomSite.label_entity_id[i] = entityBuilder.getEntityId(compId, moleculeType, asymIds.value(i));
}
const atom_site = getAtomSite(atomSite, terIndices);
const atom_site = getAtomSite(atomSite, hasTer);
if (!isPdbqt) delete atom_site.partial_charge;
if (conectRange) {

View File

@@ -65,7 +65,7 @@ export namespace ComponentBond {
return e;
}
const { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, _rowCount, pdbx_ordinal } = data;
const { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, _rowCount } = data;
let entry = addEntry(comp_id.value(0)!);
for (let i = 0; i < _rowCount; i++) {
@@ -74,7 +74,6 @@ export namespace ComponentBond {
const nameB = atom_id_2.value(i)!;
const order = value_order.value(i)!;
const aromatic = pdbx_aromatic_flag.value(i) === 'y';
const key = pdbx_ordinal.value(i);
if (entry.id !== id) {
entry = addEntry(id);
@@ -90,29 +89,29 @@ export namespace ComponentBond {
case 'quad': ord = 4; break;
}
entry.add(nameA, nameB, ord, flags, key);
entry.add(nameA, nameB, ord, flags);
}
return entries;
}
export class Entry {
readonly map: Map<string, Map<string, { order: number, flags: number, key: number }>> = new Map();
readonly map: Map<string, Map<string, { order: number, flags: number }>> = new Map();
add(a: string, b: string, order: number, flags: number, key: number, swap = true) {
add(a: string, b: string, order: number, flags: number, swap = true) {
const e = this.map.get(a);
if (e !== void 0) {
const f = e.get(b);
if (f === void 0) {
e.set(b, { order, flags, key });
e.set(b, { order, flags });
}
} else {
const map = new Map<string, { order: number, flags: number, key: number }>();
map.set(b, { order, flags, key });
const map = new Map<string, { order: number, flags: number }>();
map.set(b, { order, flags });
this.map.set(a, map);
}
if (swap) this.add(b, a, order, flags, key, false);
if (swap) this.add(b, a, order, flags, false);
}
constructor(public readonly id: string) { }

View File

@@ -22,8 +22,6 @@ import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { InteractionFlag } from '../interactions/common';
import { Unit } from '../../../mol-model/structure/structure';
import { Sphere3D } from '../../../mol-math/geometry';
import { assertUnreachable } from '../../../mol-util/type-helpers';
import { InteractionsSharedParams } from './shared';
function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InteractionsInterUnitParams>, mesh?: Mesh) {
if (!structure.hasAtomic) return Mesh.createEmpty(mesh);
@@ -33,7 +31,7 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
const { contacts, unitsFeatures } = interactions;
const { edgeCount, edges } = contacts;
const { sizeFactor, parentDisplay } = props;
const { sizeFactor } = props;
if (!edgeCount) return Mesh.createEmpty(mesh);
@@ -72,48 +70,14 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
if (child) {
const b = edges[edgeIndex];
const childUnitA = child.unitMap.get(b.unitA);
if (!childUnitA) return true;
if (parentDisplay === 'stub') {
const childUnitA = child.unitMap.get(b.unitA);
if (!childUnitA) return true;
const unitA = structure.unitMap.get(b.unitA);
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;
}
} else if (parentDisplay === 'full' || parentDisplay === 'between') {
let flagA = false;
let flagB = false;
const childUnitA = child.unitMap.get(b.unitA);
if (!childUnitA) {
flagA = true;
} else {
const unitA = structure.unitMap.get(b.unitA);
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)) flagA = true;
}
}
const childUnitB = child.unitMap.get(b.unitB);
if (!childUnitB) {
flagB = true;
} else {
const unitB = structure.unitMap.get(b.unitB);
const { offsets, members } = unitsFeatures.get(b.unitB);
for (let i = offsets[b.indexB], il = offsets[b.indexB + 1]; i < il; ++i) {
const eB = unitB.elements[members[i]];
if (!SortedArray.has(childUnitB.elements, eB)) flagB = true;
}
}
return parentDisplay === 'full' ? flagA && flagB : flagA === flagB;
} else {
assertUnreachable(parentDisplay);
const unitA = structure.unitMap.get(b.unitA);
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;
}
}
@@ -137,7 +101,10 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
export const InteractionsInterUnitParams = {
...ComplexMeshParams,
...LinkCylinderParams,
...InteractionsSharedParams,
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
includeParent: PD.Boolean(false),
};
export type InteractionsInterUnitParams = typeof InteractionsInterUnitParams
@@ -154,8 +121,7 @@ export function InteractionsInterUnitVisual(materialId: number): ComplexVisual<I
newProps.dashCount !== currentProps.dashCount ||
newProps.dashScale !== currentProps.dashScale ||
newProps.dashCap !== currentProps.dashCap ||
newProps.radialSegments !== currentProps.radialSegments ||
newProps.parentDisplay !== currentProps.parentDisplay
newProps.radialSegments !== currentProps.radialSegments
);
const interactionsHash = InteractionsProvider.get(newStructure).version;

View File

@@ -22,8 +22,6 @@ import { Interactions } from '../interactions/interactions';
import { InteractionFlag } from '../interactions/common';
import { Sphere3D } from '../../../mol-math/geometry';
import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
import { assertUnreachable } from '../../../mol-util/type-helpers';
import { InteractionsSharedParams } from './shared';
async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<InteractionsIntraUnitParams>, mesh?: Mesh) {
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
@@ -40,7 +38,7 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
const { x, y, z, members, offsets } = features;
const { edgeCount, a, b, edgeProps: { flag } } = contacts;
const { sizeFactor, parentDisplay } = props;
const { sizeFactor } = props;
if (!edgeCount) return Mesh.createEmpty(mesh);
@@ -62,31 +60,10 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
if (flag[edgeIndex] === InteractionFlag.Filtered) return true;
if (childUnit) {
if (parentDisplay === 'stub') {
const f = a[edgeIndex];
for (let i = offsets[f], il = offsets[f + 1]; i < il; ++i) {
const e = unit.elements[members[offsets[i]]];
if (!SortedArray.has(childUnit.elements, e)) return true;
}
} else if (parentDisplay === 'full' || parentDisplay === 'between') {
let flagA = false;
let flagB = false;
const fA = a[edgeIndex];
for (let i = offsets[fA], il = offsets[fA + 1]; i < il; ++i) {
const eA = unit.elements[members[offsets[i]]];
if (!SortedArray.has(childUnit.elements, eA)) flagA = true;
}
const fB = b[edgeIndex];
for (let i = offsets[fB], il = offsets[fB + 1]; i < il; ++i) {
const eB = unit.elements[members[offsets[i]]];
if (!SortedArray.has(childUnit.elements, eB)) flagB = true;
}
return parentDisplay === 'full' ? flagA && flagB : flagA === flagB;
} else {
assertUnreachable(parentDisplay);
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;
}
}
@@ -109,7 +86,10 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
export const InteractionsIntraUnitParams = {
...UnitsMeshParams,
...LinkCylinderParams,
...InteractionsSharedParams,
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
includeParent: PD.Boolean(false),
};
export type InteractionsIntraUnitParams = typeof InteractionsIntraUnitParams
@@ -126,8 +106,7 @@ export function InteractionsIntraUnitVisual(materialId: number): UnitsVisual<Int
newProps.dashCount !== currentProps.dashCount ||
newProps.dashScale !== currentProps.dashScale ||
newProps.dashCap !== currentProps.dashCap ||
newProps.radialSegments !== currentProps.radialSegments ||
newProps.parentDisplay !== currentProps.parentDisplay
newProps.radialSegments !== currentProps.radialSegments
);
const interactionsHash = InteractionsProvider.get(newStructureGroup.structure).version;

View File

@@ -1,16 +0,0 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
export const InteractionsSharedParams = {
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
includeParent: PD.Boolean(false),
parentDisplay: PD.Select('stub', PD.arrayToOptions(['stub', 'full', 'between'] as const), { description: 'Only has an effect when "includeParent" is enabled. "Stub" shows just the child side of interactions to the parent. "Full" shows both sides of interactions to the parent. "Between" shows only interactions to the parent.' }),
};
export type InteractionsSharedParams = typeof InteractionsSharedParams

View File

@@ -12,7 +12,6 @@ import * as modifiers from './query/queries/modifiers';
import * as filters from './query/queries/filters';
import * as combinators from './query/queries/combinators';
import * as internal from './query/queries/internal';
import * as atomset from './query/queries/atom-set';
import { Predicates as pred } from './query/predicates';
export const Queries = {
@@ -21,8 +20,7 @@ export const Queries = {
modifiers,
combinators,
pred,
internal,
atomset
internal
};
export { StructureSelection, StructureQuery };
export { StructureSelection, StructureQuery };

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018 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>
*/
import { Structure, StructureElement, Unit } from '../structure';
@@ -114,7 +113,6 @@ class QueryContextBondInfo<U extends Unit = Unit> {
bIndex: StructureElement.UnitIndex = 0 as StructureElement.UnitIndex;
type: BondType = BondType.Flag.None;
order: number = 0;
key: number = -1;
private testFn: QueryPredicate = defaultBondTest;

View File

@@ -1,36 +0,0 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Koya Sakuma
* Adapted from MolQL implemtation of atom-set.ts
*
* Copyright (c) 2017 MolQL contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StructureQuery } from '../query';
import { StructureSelection } from '../selection';
import { getCurrentStructureProperties } from './filters';
import { QueryContext, QueryFn } from '../context';
export function atomCount(ctx: QueryContext) {
return ctx.currentStructure.elementCount;
}
export function countQuery(query: StructureQuery) {
return (ctx: QueryContext) => {
const sel = query(ctx);
return StructureSelection.structureCount(sel);
};
}
export function propertySet(prop: QueryFn<any>) {
return (ctx: QueryContext) => {
const set = new Set();
return getCurrentStructureProperties(ctx, prop, set);
};
}

View File

@@ -1,13 +1,12 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018 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>
*/
import { SetUtils } from '../../../../mol-util/set';
import { Unit } from '../../structure';
import { QueryContext, QueryFn } from '../context';
import { QueryContext, QueryFn, QueryPredicate } from '../context';
import { StructureQuery } from '../query';
import { StructureSelection } from '../selection';
import { structureAreIntersecting } from '../utils/structure-set';
@@ -17,7 +16,7 @@ import { Structure } from '../../structure/structure';
import { StructureElement } from '../../structure/element';
import { SortedArray } from '../../../../mol-data/int';
export function pick(query: StructureQuery, pred: QueryFn<any>): StructureQuery {
export function pick(query: StructureQuery, pred: QueryPredicate): StructureQuery {
return ctx => {
const sel = query(ctx);
const ret = StructureSelection.LinearBuilder(ctx.inputStructure);
@@ -51,7 +50,9 @@ export function first(query: StructureQuery): StructureQuery {
};
}
export function getCurrentStructureProperties(ctx: QueryContext, props: QueryFn<any>, set: Set<any>) {
export interface UnitTypeProperties { atomic?: QueryFn, coarse?: QueryFn }
export function getCurrentStructureProperties(ctx: QueryContext, props: UnitTypeProperties, set: Set<any>) {
const { units } = ctx.currentStructure;
const l = ctx.pushCurrentElement();
@@ -60,9 +61,9 @@ export function getCurrentStructureProperties(ctx: QueryContext, props: QueryFn<
l.unit = unit;
const elements = unit.elements;
const fn = props;
// if (Unit.isAtomic(unit)) fn = props.atomic;
// else fn = props.coarse;
let fn;
if (Unit.isAtomic(unit)) fn = props.atomic;
else fn = props.coarse;
if (!fn) continue;
for (let j = 0, _j = elements.length; j < _j; j++) {
@@ -76,7 +77,7 @@ export function getCurrentStructureProperties(ctx: QueryContext, props: QueryFn<
return set;
}
function getSelectionProperties(ctx: QueryContext, query: StructureQuery, props: QueryFn<any>) {
function getSelectionProperties(ctx: QueryContext, query: StructureQuery, props: UnitTypeProperties) {
const set = new Set();
const sel = query(ctx);
@@ -91,7 +92,7 @@ function getSelectionProperties(ctx: QueryContext, query: StructureQuery, props:
return set;
}
export function withSameAtomProperties(query: StructureQuery, propertySource: StructureQuery, props: QueryFn<any>): StructureQuery {
export function withSameAtomProperties(query: StructureQuery, propertySource: StructureQuery, props: UnitTypeProperties): StructureQuery {
return ctx => {
const sel = query(ctx);
const propSet = getSelectionProperties(ctx, propertySource, props);
@@ -101,7 +102,7 @@ export function withSameAtomProperties(query: StructureQuery, propertySource: St
StructureSelection.forEach(sel, (s, i) => {
ctx.currentStructure = s;
const currentProps = getCurrentStructureProperties(ctx, props, new Set());
if (SetUtils.isSuperset(propSet, currentProps)) {
if (SetUtils.isSuperset(currentProps, propSet)) {
ret.add(s);
}
@@ -247,7 +248,7 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
const inputUnit = input.unitMap.get(unit.id) as Unit.Atomic;
const { offset, b, edgeProps: { flags, order, key } } = inputUnit.bonds;
const { offset, b, edgeProps: { flags, order } } = inputUnit.bonds;
const bondedUnits = interBonds.getConnectedUnits(unit.id);
const buCount = bondedUnits.length;
@@ -272,7 +273,6 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
atomicBond.bIndex = b[l] as StructureElement.UnitIndex;
atomicBond.type = flags[l];
atomicBond.order = order[l];
atomicBond.key = key[l];
if (atomicBond.test(queryCtx, true)) return true;
}
@@ -295,7 +295,6 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
atomicBond.bIndex = bond.indexB;
atomicBond.type = bond.props.flag;
atomicBond.order = bond.props.order;
atomicBond.key = bond.props.key;
if (atomicBond.test(queryCtx, true)) return true;
}
}
@@ -343,4 +342,4 @@ export function isConnectedTo({ query, target, disjunct, invert, bondTest }: IsC
return ret.getSelection();
};
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 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>
@@ -322,7 +322,7 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
for (const unit of structure.units) {
if (unit.kind !== Unit.Kind.Atomic) continue;
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order, key } } = unit.bonds;
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order } } = unit.bonds;
atomicBond.a.unit = unit;
atomicBond.b.unit = unit;
for (let i = 0 as StructureElement.UnitIndex, _i = unit.elements.length; i < _i; i++) {
@@ -335,7 +335,6 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
atomicBond.b.element = unit.elements[intraBondB[lI]];
atomicBond.type = flags[lI];
atomicBond.order = order[lI];
atomicBond.key = key[lI];
// No need to "swap test" because each bond direction will be visited eventually.
if (atomicBond.test(ctx, false)) {
const b = structure.subsetBuilder(false);
@@ -359,7 +358,6 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
atomicBond.bIndex = bond.indexB;
atomicBond.order = bond.props.order;
atomicBond.type = bond.props.flag;
atomicBond.key = bond.props.key;
// No need to "swap test" because each bond direction will be visited eventually.
if (atomicBond.test(ctx, false)) {

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2020 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>
*/
import { Segmentation, SortedArray } from '../../../../mol-data/int';
@@ -371,7 +370,7 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
}
const inputUnitA = inputStructure.unitMap.get(unit.id) as Unit.Atomic;
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order, key } } = inputUnitA.bonds;
const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order } } = inputUnitA.bonds;
atomicBond.setStructure(inputStructure);
@@ -398,7 +397,6 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
atomicBond.b.element = bElement;
atomicBond.type = flags[lI];
atomicBond.order = order[lI];
atomicBond.key = key[lI];
if (atomicBond.test(ctx, true)) {
builder.addToUnit(unit.id, bElement);
@@ -429,7 +427,6 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
atomicBond.b.element = bElement;
atomicBond.type = bond.props.flag;
atomicBond.order = bond.props.order;
atomicBond.key = bond.props.key;
if (atomicBond.test(ctx, true)) {
builder.addToUnit(bondedUnit.unitB, bElement);

View File

@@ -15,17 +15,16 @@ import { InterUnitGraph } from '../../../../../mol-math/graph/inter-unit-graph';
type IntraUnitBonds = IntAdjacencyGraph<StructureElement.UnitIndex, {
readonly order: ArrayLike<number>,
readonly flags: ArrayLike<BondType.Flag>
readonly key: ArrayLike<number>,
}, {
/** can remap even with dynamicBonds on, e.g., for water molecules */
readonly canRemap?: boolean
}>
namespace IntraUnitBonds {
export const Empty: IntraUnitBonds = IntAdjacencyGraph.create([], [], [], 0, { flags: [], order: [], key: [] });
export const Empty: IntraUnitBonds = IntAdjacencyGraph.create([], [], [], 0, { flags: [], order: [] });
}
type InterUnitEdgeProps = { readonly order: number, readonly flag: BondType.Flag, readonly key: number }
type InterUnitEdgeProps = { readonly order: number, readonly flag: BondType.Flag }
class InterUnitBonds extends InterUnitGraph<number, StructureElement.UnitIndex, InterUnitEdgeProps> {
/** Get inter-unit bond given a bond-location */

View File

@@ -80,7 +80,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
if (!props.forceCompute && indexPairs) {
const { maxDistance } = indexPairs;
const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
const { offset, b, edgeProps: { order, distance, flag } } = indexPairs.bonds;
const srcA = sourceIndex.value(aI);
const aeI = getElementIdx(type_symbolA.value(aI));
@@ -113,7 +113,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
}
if (add) {
builder.add(_aI, _bI, { order: order[i], flag: flag[i], key: key[i] });
builder.add(_aI, _bI, { order: order[i], flag: flag[i] });
}
}
continue; // assume `indexPairs` supplies all bonds
@@ -131,7 +131,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
// check if the bond is within MAX_RADIUS for this pair of units
if (getDistance(unitA, aI, unitB, p.atomIndex) > maxRadius) continue;
builder.add(_aI, _bI, { order: se.order, flag: se.flags, key: se.rowIndex });
builder.add(_aI, _bI, { order: se.order, flag: se.flags });
added = true;
}
// assume, for an atom, that if any inter unit bond is given
@@ -187,8 +187,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput
const compIdB = label_comp_idB.value(residueIndexB[bI]);
builder.add(_aI, _bI, {
order: getInterBondOrderFromTable(compIdA, compIdB, atomIdA, atomIdB),
flag: (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed,
key: -1
flag: (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed
});
}
}

View File

@@ -24,19 +24,17 @@ import { Model } from '../../../model/model';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const v3distance = Vec3.distance;
function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], _key: number[], atomCount: number, canRemap: boolean): IntraUnitBonds {
function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], atomCount: number, canRemap: boolean): IntraUnitBonds {
const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB);
const flags = new Uint16Array(builder.slotCount);
const order = new Int8Array(builder.slotCount);
const key = new Uint32Array(builder.slotCount);
for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
builder.addNextEdge();
builder.assignProperty(flags, _flags[i]);
builder.assignProperty(order, _order[i]);
builder.assignProperty(key, _key[i]);
}
return builder.createGraph({ flags, order, key }, { canRemap });
return builder.createGraph({ flags, order }, { canRemap });
}
const tmpDistVecA = Vec3();
@@ -55,7 +53,7 @@ function findIndexPairBonds(unit: Unit.Atomic) {
const { type_symbol } = unit.model.atomicHierarchy.atoms;
const atomCount = unit.elements.length;
const { maxDistance } = indexPairs;
const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds;
const { offset, b, edgeProps: { order, distance, flag } } = indexPairs.bonds;
const { atomSourceIndex: sourceIndex } = unit.model.atomicHierarchy;
const { invertedIndex } = Model.getInvertedAtomSourceIndex(unit.model);
@@ -64,7 +62,6 @@ function findIndexPairBonds(unit: Unit.Atomic) {
const atomB: StructureElement.UnitIndex[] = [];
const flags: number[] = [];
const orders: number[] = [];
const keys: number[] = [];
for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
const aI = atoms[_aI];
@@ -107,12 +104,11 @@ function findIndexPairBonds(unit: Unit.Atomic) {
atomB[atomB.length] = _bI;
orders[orders.length] = order[i];
flags[flags.length] = flag[i];
keys[keys.length] = key[i];
}
}
}
return getGraph(atomA, atomB, orders, flags, keys, atomCount, false);
return getGraph(atomA, atomB, orders, flags, atomCount, false);
}
function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBonds {
@@ -136,10 +132,9 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
const atomB: StructureElement.UnitIndex[] = [];
const flags: number[] = [];
const order: number[] = [];
const key: number[] = [];
let lastResidue = -1;
let componentMap: Map<string, Map<string, { flags: number, order: number, key: number }>> | undefined = void 0;
let componentMap: Map<string, Map<string, { flags: number, order: number }>> | undefined = void 0;
let isWatery = true, isDictionaryBased = true, isSequenced = true;
@@ -167,7 +162,6 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
atomB[atomB.length] = _bI;
flags[flags.length] = se.flags;
order[order.length] = se.order;
key[key.length] = se.rowIndex;
if (!hasStructConn) structConnAdded.clear();
hasStructConn = true;
@@ -236,7 +230,6 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
flag |= BondType.Flag.MetallicCoordination;
}
flags[flags.length] = flag;
key[key.length] = e.key;
}
continue;
}
@@ -250,7 +243,6 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
atomB[atomB.length] = _bI;
order[order.length] = getIntraBondOrderFromTable(compId, atomIdA, label_atom_id.value(bI));
flags[flags.length] = (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed;
key[key.length] = -1;
const seqIdB = label_seq_id.value(rbI);
@@ -261,7 +253,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
}
const canRemap = isWatery || (isDictionaryBased && isSequenced);
return getGraph(atomA, atomB, order, flags, key, atomCount, canRemap);
return getGraph(atomA, atomB, order, flags, atomCount, canRemap);
}
function computeIntraUnitBonds(unit: Unit.Atomic, props?: Partial<BondComputationProps>) {

View File

@@ -8,8 +8,7 @@
import { Segmentation } from '../../../../mol-data/int';
import { MinimizeRmsd } from '../../../../mol-math/linear-algebra/3d/minimize-rmsd';
import { SIFTSMapping } from '../../../../mol-model-props/sequence/sifts-mapping';
import { ElementIndex, ResidueIndex } from '../../model/indexing';
import { StructureElement } from '../element';
import { ElementIndex } from '../../model/indexing';
import { Structure } from '../structure';
import { Unit } from '../unit';
@@ -25,16 +24,11 @@ export interface AlignmentResult {
failedPairs: [number, number][]
}
type IncludeResidueTest = (traceElementOrFirstAtom: StructureElement.Location<Unit.Atomic>, residueIndex: ResidueIndex, startIndex: ElementIndex, endIndex: ElementIndex) => boolean
export function alignAndSuperposeWithSIFTSMapping(
structures: Structure[],
options?: { traceOnly?: boolean, includeResidueTest?: IncludeResidueTest }
): AlignmentResult {
export function alignAndSuperposeWithSIFTSMapping(structures: Structure[], options?: { traceOnly?: boolean }): AlignmentResult {
const indexMap = new Map<string, IndexEntry>();
for (let i = 0; i < structures.length; i++) {
buildIndex(structures[i], indexMap, i, options?.traceOnly ?? true, options?.includeResidueTest ?? _includeAllResidues);
buildIndex(structures[i], indexMap, i, options?.traceOnly ?? true);
}
const index = Array.from(indexMap.values());
@@ -143,16 +137,11 @@ interface IndexEntry {
pivots: { [i: number]: [unit: Unit.Atomic, start: ElementIndex, end: ElementIndex] | undefined }
}
function _includeAllResidues() { return true; }
function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: number, traceOnly: boolean, includeTest: IncludeResidueTest) {
const loc = StructureElement.Location.create<Unit.Atomic>(structure);
function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: number, traceOnly: boolean) {
for (const unit of structure.units) {
if (unit.kind !== Unit.Kind.Atomic) continue;
const { elements, model } = unit;
loc.unit = unit;
const map = SIFTSMapping.Provider.get(model).value;
if (!map) return;
@@ -172,11 +161,9 @@ function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: nu
if (!dbName[rI]) continue;
const traceElement = traceElementIndex[rI];
let start, end;
if (traceOnly) {
start = traceElement;
start = traceElementIndex[rI];
if (start === -1) continue;
end = start + 1 as ElementIndex;
} else {
@@ -184,9 +171,6 @@ function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: nu
end = elements[residueSegment.end - 1] + 1 as ElementIndex;
}
loc.element = (traceElement >= 0 ? traceElement : start) as ElementIndex;
if (!includeTest(loc, rI, start, end)) continue;
const key = `${dbName[rI]}-${accession[rI]}-${num[rI]}`;
if (!index.has(key)) {

View File

@@ -90,7 +90,6 @@ const DownloadStructure = StateAction.build({
url: PD.Url(''),
format: PD.Select<BuiltInTrajectoryFormat>('mmcif', PD.arrayToOptions(BuiltInTrajectoryFormats.map(f => f[0]), f => f)),
isBinary: PD.Boolean(false),
label: PD.Optional(PD.Text('')),
options
}, { isFlat: true, label: 'URL' })
})
@@ -105,7 +104,7 @@ const DownloadStructure = StateAction.build({
switch (src.name) {
case 'url':
downloadParams = [{ url: src.params.url, isBinary: src.params.isBinary, label: src.params.label || undefined }];
downloadParams = [{ url: src.params.url, isBinary: src.params.isBinary }];
format = src.params.format;
break;
case 'pdb':

View File

@@ -41,10 +41,8 @@ export namespace StructureRepresentationPresetProvider {
quality: PD.Optional(PD.Select<VisualQuality>('auto', VisualQualityOptions)),
theme: PD.Optional(PD.Group({
globalName: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
globalColorParams: PD.Optional(PD.Value<any>({}, { isHidden: true })),
carbonColor: PD.Optional(PD.Select('chain-id', PD.arrayToOptions(['chain-id', 'operator-name', 'element-symbol'] as const))),
symmetryColor: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
symmetryColorParams: PD.Optional(PD.Value<any>({}, { isHidden: true })),
focus: PD.Optional(PD.Group({
name: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
params: PD.Optional(PD.Value<ColorTheme.BuiltInParams<ColorTheme.BuiltIn>>({} as any))
@@ -78,15 +76,13 @@ export namespace StructureRepresentationPresetProvider {
if (params.ignoreLight !== void 0) typeParams.ignoreLight = !!params.ignoreLight;
const color: ColorTheme.BuiltIn | undefined = params.theme?.globalName ? params.theme?.globalName : void 0;
const ballAndStickColor: ColorTheme.BuiltInParams<'element-symbol'> = params.theme?.carbonColor !== undefined
? { carbonColor: getCarbonColorParams(params.theme?.carbonColor), ...params.theme?.globalColorParams }
: { ...params.theme?.globalColorParams };
? { carbonColor: getCarbonColorParams(params.theme?.carbonColor) }
: { };
const symmetryColor: ColorTheme.BuiltIn | undefined = structure && params.theme?.symmetryColor
? isSymmetry(structure) ? params.theme?.symmetryColor : color
: color;
const symmetryColorParams = params.theme?.symmetryColorParams ? { ...params.theme?.globalColorParams, ...params.theme?.symmetryColorParams } : { ...params.theme?.globalColorParams };
const globalColorParams = params.theme?.globalColorParams ? { ...params.theme?.globalColorParams } : undefined;
return { update, builder, color, symmetryColor, symmetryColorParams, globalColorParams, typeParams, ballAndStickColor };
return { update, builder, color, symmetryColor, typeParams, ballAndStickColor };
}
export function updateFocusRepr<T extends ColorTheme.BuiltIn>(plugin: PluginContext, structure: Structure, themeName: T | undefined, themeParams: ColorTheme.BuiltInParams<T> | undefined) {
@@ -181,18 +177,18 @@ const polymerAndLigand = StructureRepresentationPresetProvider({
const waterType = (components.water?.obj?.data?.elementCount || 0) > 50_000 ? 'line' : 'ball-and-stick';
const lipidType = (components.lipid?.obj?.data?.elementCount || 0) > 20_000 ? 'line' : 'ball-and-stick';
const { update, builder, typeParams, color, symmetryColor, symmetryColorParams, globalColorParams, ballAndStickColor } = reprBuilder(plugin, params, structure);
const { update, builder, typeParams, color, symmetryColor, ballAndStickColor } = reprBuilder(plugin, params, structure);
const representations = {
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'polymer' }),
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor }, { tag: 'polymer' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color, colorParams: ballAndStickColor }, { tag: 'ligand' }),
nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color, colorParams: ballAndStickColor }, { tag: 'non-standard' }),
branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.3 }, color, colorParams: ballAndStickColor }, { tag: 'branched-ball-and-stick' }),
branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color, colorParams: globalColorParams }, { tag: 'branched-snfg-3d' }),
water: builder.buildRepresentation(update, components.water, { type: waterType, typeParams: { ...typeParams, alpha: 0.6, visuals: waterType === 'line' ? ['intra-bond', 'element-point'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams } }, { tag: 'water' }),
ion: builder.buildRepresentation(update, components.ion, { type: 'ball-and-stick', typeParams, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams } }, { tag: 'ion' }),
lipid: builder.buildRepresentation(update, components.lipid, { type: lipidType, typeParams: { ...typeParams, alpha: 0.6, visuals: lipidType === 'line' ? ['intra-bond'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams } }, { tag: 'lipid' }),
coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'chain-id', colorParams: globalColorParams }, { tag: 'coarse' })
branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }, { tag: 'branched-snfg-3d' }),
water: builder.buildRepresentation(update, components.water, { type: waterType, typeParams: { ...typeParams, alpha: 0.6, visuals: waterType === 'line' ? ['intra-bond', 'element-point'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'water' }),
ion: builder.buildRepresentation(update, components.ion, { type: 'ball-and-stick', typeParams, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ion' }),
lipid: builder.buildRepresentation(update, components.lipid, { type: lipidType, typeParams: { ...typeParams, alpha: 0.6, visuals: lipidType === 'line' ? ['intra-bond'] : undefined }, color, colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'lipid' }),
coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'chain-id' }, { tag: 'coarse' })
};
await update.commit({ revertOnError: false });
@@ -227,11 +223,11 @@ const proteinAndNucleic = StructureRepresentationPresetProvider({
smoothness: structure.isCoarseGrained ? 1.0 : 1.5,
};
const { update, builder, typeParams, symmetryColor, symmetryColorParams } = reprBuilder(plugin, params, structure);
const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
const representations = {
protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'protein' }),
nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'nucleic' })
protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor }, { tag: 'protein' }),
nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'nucleic' })
};
await update.commit({ revertOnError: true });
@@ -279,11 +275,11 @@ const coarseSurface = StructureRepresentationPresetProvider({
});
}
const { update, builder, typeParams, symmetryColor, symmetryColorParams } = reprBuilder(plugin, params, structure);
const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
const representations = {
polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'polymer' }),
lipid: builder.buildRepresentation(update, components.lipid, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'lipid' })
polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' }),
lipid: builder.buildRepresentation(update, components.lipid, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'lipid' })
};
await update.commit({ revertOnError: true });
@@ -313,10 +309,10 @@ const polymerCartoon = StructureRepresentationPresetProvider({
sizeFactor: structure.isCoarseGrained ? 0.8 : 0.2
};
const { update, builder, typeParams, symmetryColor, symmetryColorParams } = reprBuilder(plugin, params, structure);
const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
const representations = {
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor, colorParams: symmetryColorParams }, { tag: 'polymer' })
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color: symmetryColor }, { tag: 'polymer' })
};
await update.commit({ revertOnError: true });
@@ -371,9 +367,9 @@ const atomicDetail = StructureRepresentationPresetProvider({
});
}
const { update, builder, typeParams, color, ballAndStickColor, globalColorParams } = reprBuilder(plugin, params, structure);
const { update, builder, typeParams, color, ballAndStickColor } = reprBuilder(plugin, params, structure);
const colorParams = lowResidueElementRatio && !bondsGiven
? { carbonColor: { name: 'element-symbol', params: {} }, ...globalColorParams }
? { carbonColor: { name: 'element-symbol', params: {} } }
: ballAndStickColor;
const representations = {
@@ -381,7 +377,7 @@ const atomicDetail = StructureRepresentationPresetProvider({
};
if (showCarbohydrateSymbol) {
Object.assign(representations, {
snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color, colorParams: globalColorParams }, { tag: 'snfg-3d' }),
snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color }, { tag: 'snfg-3d' }),
});
}

View File

@@ -90,7 +90,7 @@ export class StructureRepresentationBuilder {
}
applyPreset<K extends keyof PresetStructureRepresentations>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, preset: K, params?: StructureRepresentationPresetProvider.Params<PresetStructureRepresentations[K]>): Promise<StructureRepresentationPresetProvider.State<PresetStructureRepresentations[K]>> | undefined
applyPreset<P = any, S extends {} = {}>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, provider: StructureRepresentationPresetProvider<P, S>, params?: P): Promise<S> | undefined
applyPreset<P = any, S = {}>(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, provider: StructureRepresentationPresetProvider<P, S>, params?: P): Promise<S> | undefined
applyPreset(parent: StateObjectRef<PluginStateObject.Molecule.Structure>, providerId: string, params?: any): Promise<any> | undefined
applyPreset(parent: StateObjectRef, providerRef: string | StructureRepresentationPresetProvider, params?: any): Promise<any> | undefined {
const provider = this.resolveProvider(providerRef);

View File

@@ -42,7 +42,7 @@ export class PluginComponent {
}
}
export class StatefulPluginComponent<State extends {}> extends PluginComponent {
export class StatefulPluginComponent<State> extends PluginComponent {
private _state: State;
protected updateState(...states: Partial<State>[]): boolean {

View File

@@ -75,7 +75,7 @@ export const CifCoreProvider: TrajectoryFormatProvider = {
visuals: defaultVisuals
};
function directTrajectory<P extends {}>(transformer: StateTransformer<PluginStateObject.Data.String | PluginStateObject.Data.Binary, PluginStateObject.Molecule.Trajectory, P>, transformerParams?: P): TrajectoryFormatProvider['parse'] {
function directTrajectory<P>(transformer: StateTransformer<PluginStateObject.Data.String | PluginStateObject.Data.Binary, PluginStateObject.Molecule.Trajectory, P>, transformerParams?: P): TrajectoryFormatProvider['parse'] {
return async (plugin, data, params) => {
const state = plugin.state.data;
const trajectory = await state.build().to(data)

View File

@@ -1079,4 +1079,4 @@ const ShapeFromPly = PluginStateTransform.BuiltIn({
return new SO.Shape.Provider(shape, props);
});
}
});
});

View File

@@ -13,7 +13,7 @@ import { Icon, ArrowRightSvg, ArrowDropDownSvg } from './controls/icons';
export const PluginReactContext = React.createContext(void 0 as any as PluginUIContext);
export abstract class PluginUIComponent<P extends {} = {}, S = {}, SS = {}> extends React.Component<P & { children?: any }, S, SS> {
export abstract class PluginUIComponent<P = {}, S = {}, SS = {}> extends React.Component<P & { children?: any }, S, SS> {
static contextType = PluginReactContext;
readonly plugin: PluginUIContext;

View File

@@ -7,7 +7,6 @@
import * as React from 'react';
import { Mat4, Vec2, Vec3 } from '../../mol-math/linear-algebra';
import { Script } from '../../mol-script/script';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { ColorListEntry } from '../../mol-util/color/color';
@@ -23,7 +22,7 @@ import { PluginUIContext } from '../context';
import { ActionMenu } from './action-menu';
import { ColorOptions, ColorValueOption, CombinedColorControl } from './color';
import { Button, ControlGroup, ControlRow, ExpandGroup, IconButton, TextInput, ToggleButton } from './common';
import { ArrowDownwardSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowUpwardSvg, BookmarksOutlinedSvg, CheckSvg, ClearSvg, DeleteOutlinedSvg, HelpOutlineSvg, Icon, MoreHorizSvg, WarningSvg } from './icons';
import { ArrowDownwardSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowUpwardSvg, BookmarksOutlinedSvg, CheckSvg, ClearSvg, DeleteOutlinedSvg, HelpOutlineSvg, Icon, MoreHorizSvg } from './icons';
import { legendFor } from './legend';
import { LineGraphComponent } from './line-graph/line-graph-component';
import { Slider, Slider2 } from './slider';
@@ -1467,38 +1466,31 @@ export class ConvertedControl extends React.PureComponent<ParamProps<PD.Converte
}
}
export class ScriptControl extends React.PureComponent<ParamProps<PD.Script>> {
onChange: ParamOnChange = ({ name, value }) => {
const k = name as 'language' | 'expression';
if (value !== this.props.value[k]) {
this.props.onChange({ param: this.props.param, name: this.props.name, value: { ...this.props.value, [k]: value } });
export class ScriptControl extends SimpleParam<PD.Script> {
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
if (value !== this.props.value.expression) {
this.update({ language: this.props.value.language, expression: value });
}
};
render() {
onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if ((e.keyCode === 13 || e.charCode === 13 || e.key === 'Enter')) {
if (this.props.onEnter) this.props.onEnter();
}
e.stopPropagation();
};
renderControl() {
// TODO: improve!
const selectParam: PD.Select<PD.Script['defaultValue']['language']> = {
defaultValue: this.props.value.language,
options: PD.objectToOptions(Script.Info),
type: 'select',
};
const select = <SelectControl param={selectParam}
isDisabled={this.props.isDisabled} onChange={this.onChange} onEnter={this.props.onEnter}
name='language' value={this.props.value.language} />;
const textParam: PD.Text = {
defaultValue: this.props.value.language,
type: 'text',
};
const text = <TextControl param={textParam} isDisabled={this.props.isDisabled} onChange={this.onChange} name='expression' value={this.props.value.expression} />;
return <>
{select}
{this.props.value.language !== 'mol-script' && <div className='msp-help-text' style={{ padding: '10px' }}>
<Icon svg={WarningSvg} /> Support for PyMOL, VMD, and Jmol selections is an experimental feature and may not always work as intended.
</div>}
{text}
</>;
const placeholder = this.props.param.label || camelCaseToWords(this.props.name);
return <input type='text'
value={this.props.value.expression || ''}
placeholder={placeholder}
onChange={this.onChange}
onKeyPress={this.props.onEnter ? this.onKeyPress : void 0}
disabled={this.props.isDisabled}
/>;
}
}
}

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020 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>
*/
import * as React from 'react';
@@ -26,7 +25,7 @@ export interface ScreenshotPreviewProps {
const _ScreenshotPreview = (props: ScreenshotPreviewProps) => {
const { plugin, cropFrameColor } = props;
const helper = plugin.helpers.viewportScreenshot;
const helper = plugin.helpers.viewportScreenshot!;
const [currentCanvas, setCurrentCanvas] = useState<HTMLCanvasElement | null>(null);
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const propsRef = useRef(props);
@@ -71,8 +70,8 @@ const _ScreenshotPreview = (props: ScreenshotPreviewProps) => {
subscribe(plugin.state.data.behaviors.isUpdating, v => {
if (!v) isDirty = true;
});
subscribe(helper?.behaviors.values, () => isDirty = true);
subscribe(helper?.behaviors.cropParams, () => isDirty = true);
subscribe(helper.behaviors.values, () => isDirty = true);
subscribe(helper.behaviors.cropParams, () => isDirty = true);
let resizeObserver: any = void 0;
if (typeof ResizeObserver !== 'undefined') {
@@ -109,9 +108,7 @@ export const ScreenshotPreview = React.memo(_ScreenshotPreview, (prev, next) =>
declare const ResizeObserver: any;
function drawPreview(helper: ViewportScreenshotHelper | undefined, target: HTMLCanvasElement, customBackground?: string, borderColor?: string, borderWidth?: number) {
if (!helper) return;
function drawPreview(helper: ViewportScreenshotHelper, target: HTMLCanvasElement, customBackground?: string, borderColor?: string, borderWidth?: number) {
const { canvas, width, height } = helper.getPreview()!;
const ctx = target.getContext('2d');
if (!ctx) return;
@@ -154,9 +151,9 @@ function drawPreview(helper: ViewportScreenshotHelper | undefined, target: HTMLC
function ViewportFrame({ plugin, canvas, color = 'rgba(255, 87, 45, 0.75)' }: { plugin: PluginContext, canvas: HTMLCanvasElement | null, color?: string }) {
const helper = plugin.helpers.viewportScreenshot;
const params = useBehavior(helper?.behaviors.values);
const cropParams = useBehavior(helper?.behaviors.cropParams);
const crop = useBehavior(helper?.behaviors.relativeCrop);
const params = useBehavior(helper?.behaviors.values!);
const cropParams = useBehavior(helper?.behaviors.cropParams!);
const crop = useBehavior(helper?.behaviors.relativeCrop!);
const cropFrameRef = useRef<Viewport>({ x: 0, y: 0, width: 0, height: 0 });
useBehavior(params?.resolution.name === 'viewport' ? plugin.canvas3d?.resized : void 0);
@@ -164,7 +161,7 @@ function ViewportFrame({ plugin, canvas, color = 'rgba(255, 87, 45, 0.75)' }: {
const [start, setStart] = useState([0, 0]);
const [current, setCurrent] = useState([0, 0]);
if (!helper || !canvas || !crop) return null;
if (!helper || !canvas) return null;
const { width, height } = helper.getSizeAndViewport();
@@ -270,7 +267,7 @@ function ViewportFrame({ plugin, canvas, color = 'rgba(255, 87, 45, 0.75)' }: {
function finish() {
const cropFrame = cropFrameRef.current;
if (cropParams?.auto) {
if (cropParams.auto) {
helper?.behaviors.cropParams.next({ ...cropParams, auto: false });
}
helper?.behaviors.relativeCrop.next({

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Adam Midlik <midlik@gmail.com>
*/
import { PluginUIComponent } from '../base';
@@ -200,9 +199,6 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
const viewParams = { ...oldView };
if (value.name === 'selection-box') {
viewParams.radius = value.params.radius;
} else if (value.name === 'camera-target') {
viewParams.radius = value.params.radius;
viewParams.dynamicDetailLevel = value.params.dynamicDetailLevel;
} else if (value.name === 'box') {
viewParams.bottomLeft = value.params.bottomLeft;
viewParams.topRight = value.params.topRight;
@@ -244,23 +240,13 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
const pivot = isEM ? 'em' : '2fo-fc';
const params = this.props.params as VolumeStreaming.Params;
const entry = (this.props.info.params as VolumeStreaming.ParamDefinition)
.entry.map(params.entry.name) as PD.Group<VolumeStreaming.EntryParamDefinition>;
const entry = ((this.props.info.params as VolumeStreaming.ParamDefinition)
.entry.map(params.entry.name) as PD.Group<VolumeStreaming.EntryParamDefinition>);
const detailLevel = entry.params.detailLevel;
const dynamicDetailLevel = {
...detailLevel,
label: 'Dynamic Detail',
defaultValue: (entry.params.view as any).map('camera-target').params.dynamicDetailLevel.defaultValue,
};
const selectionDetailLevel = {
...detailLevel,
label: 'Selection Detail',
defaultValue: (entry.params.view as any).map('auto').params.selectionDetailLevel.defaultValue,
};
const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as Volume.IsoValue).kind === 'relative';
const sampling = b.info.header.sampling[0];
const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as Volume.IsoValue).kind === 'relative';
const isRelativeParam = PD.Boolean(isRelative, { description: 'Use normalized or absolute isocontour scale.', label: 'Normalized' });
const isUnbounded = !!(params.entry.params.view.params as any).isUnbounded;
@@ -288,13 +274,6 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
isRelative: isRelativeParam,
isUnbounded: isUnboundedParam,
}, { description: 'Box around focused element.' }),
'camera-target': PD.Group({
radius: PD.Numeric(0.5, { min: 0, max: 1, step: 0.05 }, { description: 'Radius within which the volume is shown (relative to the field of view).' }),
detailLevel: { ...detailLevel, isHidden: true },
dynamicDetailLevel: dynamicDetailLevel,
isRelative: isRelativeParam,
isUnbounded: isUnboundedParam,
}, { description: 'Box around camera target.' }),
'cell': PD.Group({
detailLevel,
isRelative: isRelativeParam,
@@ -303,11 +282,12 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
'auto': PD.Group({
radius: PD.Numeric(5, { min: 0, max: 50, step: 0.5 }, { description: 'Radius in \u212B within which the volume is shown.' }),
detailLevel,
selectionDetailLevel: selectionDetailLevel,
selectionDetailLevel: { ...detailLevel, label: 'Selection Detail' },
isRelative: isRelativeParam,
isUnbounded: isUnboundedParam,
}, { description: 'Box around focused element.' }),
}, { options: VolumeStreaming.ViewTypeOptions, description: 'Controls what of the volume is displayed. "Off" hides the volume alltogether. "Bounded box" shows the volume inside the given box. "Around Focus" shows the volume around the element/atom last interacted with. "Around Camera" shows the volume around the point the camera is targeting. "Whole Structure" shows the volume for the whole structure.' })
// 'auto': PD.Group({ }), // TODO based on camera distance/active selection/whatever, show whole structure or slice.
}, { options: VolumeStreaming.ViewTypeOptions, description: 'Controls what of the volume is displayed. "Off" hides the volume alltogether. "Bounded box" shows the volume inside the given box. "Around Focus" shows the volume around the element/atom last interacted with. "Whole Structure" shows the volume for the whole structure.' })
};
const options = {
entry: params.entry.name,
@@ -319,7 +299,6 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
bottomLeft: (params.entry.params.view.params as any).bottomLeft,
topRight: (params.entry.params.view.params as any).topRight,
selectionDetailLevel: (params.entry.params.view.params as any).selectionDetailLevel,
dynamicDetailLevel: (params.entry.params.view.params as any).dynamicDetailLevel,
isRelative,
isUnbounded
}

View File

@@ -141,13 +141,11 @@ class FullSettings extends PluginUIComponent {
this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
this.subscribe(this.plugin.layout.events.updated, () => this.forceUpdate());
if (this.plugin.canvas3d) {
this.subscribe(this.plugin.canvas3d.camera.stateChanged, state => {
if (state.radiusMax !== undefined || state.radius !== undefined) {
this.forceUpdate();
}
});
}
this.subscribe(this.plugin.canvas3d!.camera.stateChanged, state => {
if (state.radiusMax !== undefined || state.radius !== undefined) {
this.forceUpdate();
}
});
}
render() {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2021 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>
@@ -288,12 +288,7 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
for (const loci of this.lociArray) {
this.plugin.managers.interactivity.lociHighlights.highlight({ loci }, false);
}
const reprLocis = this.props.cell.obj?.data.repr.getAllLoci();
if (reprLocis) {
for (const loci of reprLocis) {
this.plugin.managers.interactivity.lociHighlights.highlight({ loci }, false);
}
}
this.plugin.managers.interactivity.lociHighlights.highlight({ loci: this.props.cell.obj?.data.repr.getLoci()! }, false);
};
clearHighlight = () => {

View File

@@ -59,7 +59,7 @@ export class ViewportCanvas extends PluginUIComponent<ViewportCanvasParams, View
return <div className='msp-no-webgl'>
<div>
<p><b>WebGL does not seem to be available.</b></p>
<p>This can be caused by an outdated browser, graphics card driver issue, or bad weather. Sometimes, just restarting the browser helps. Also, make sure hardware acceleration is enabled in your browser.</p>
<p>This can be caused by an outdated browser, graphics card driver issue, or bad weather. Sometimes, just restarting the browser helps.</p>
<p>For a list of supported browsers, refer to <a href='http://caniuse.com/#feat=webgl' target='_blank'>http://caniuse.com/#feat=webgl</a>.</p>
</div>
</div>;

View File

@@ -7,15 +7,14 @@
import * as React from 'react';
import { Binding } from '../../mol-util/binding';
import { PluginUIComponent } from '../base';
import { StateTransformer, StateSelection, State } from '../../mol-state';
import { StateTransformer, StateSelection } from '../../mol-state';
import { SelectLoci } from '../../mol-plugin/behavior/dynamic/representation';
import { FocusLoci } from '../../mol-plugin/behavior/dynamic/representation';
import { Icon, ArrowDropDownSvg, ArrowRightSvg, CameraSvg } from '../controls/icons';
import { Button } from '../controls/common';
import { memoizeLatest } from '../../mol-util/memoize';
function getBindingsList(bindings: { [k: string]: Binding }) {
return Object.keys(bindings).map(k => [k, bindings[k]] as [string, Binding]).filter(b => Binding.isBinding(b[1]));
return Object.keys(bindings).map(k => [k, bindings[k]] as [string, Binding]);
}
export class BindingsHelp extends React.PureComponent<{ bindings: { [k: string]: Binding } }> {
@@ -78,30 +77,19 @@ export class ViewportHelpContent extends PluginUIComponent<{ selectOnly?: boolea
this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
}
getInteractionBindings = memoizeLatest((cells: State.Cells) => {
let interactionBindings: { [k: string]: Binding } | undefined = void 0;
cells.forEach(c => {
const params = c.params?.values;
if (params?.bindings && Object.keys(params.bindings).length > 0) {
if (!interactionBindings) interactionBindings = { };
Object.assign(interactionBindings, params.bindings);
}
});
return interactionBindings;
});
render() {
const interactionBindings = this.getInteractionBindings(this.plugin.state.behaviors.cells);
const interactionBindings: { [k: string]: Binding } = {};
this.plugin.spec.behaviors.forEach(b => {
const { bindings } = b.defaultParams;
if (bindings) Object.assign(interactionBindings, bindings);
});
return <>
{(!this.props.selectOnly && this.plugin.canvas3d) && <HelpGroup key='trackball' header='Moving in 3D'>
<BindingsHelp bindings={this.plugin.canvas3d.props.trackball.bindings} />
</HelpGroup>}
{!!interactionBindings && <HelpGroup key='interactions' header='Mouse Controls'>
<HelpGroup key='interactions' header='Mouse Controls'>
<BindingsHelp bindings={interactionBindings} />
</HelpGroup>}
</HelpGroup>
</>;
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 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>
@@ -96,21 +96,18 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
}
function ScreenshotParams({ plugin, isDisabled }: { plugin: PluginContext, isDisabled: boolean }) {
const helper = plugin.helpers.viewportScreenshot;
const values = useBehavior(helper?.behaviors.values);
if (!helper) return null;
const helper = plugin.helpers.viewportScreenshot!;
const values = useBehavior(helper.behaviors.values);
return <ParameterControls params={helper.params} values={values} onChangeValues={v => helper.behaviors.values.next(v)} isDisabled={isDisabled} />;
}
function CropControls({ plugin }: { plugin: PluginContext }) {
const helper = plugin.helpers.viewportScreenshot;
const cropParams = useBehavior(helper?.behaviors.cropParams);
const cropParams = useBehavior(helper?.behaviors.cropParams!);
useBehavior(helper?.behaviors.relativeCrop);
if (!helper || !cropParams) return null;
if (!helper) return null;
return <div style={{ width: '100%', height: '24px', marginTop: '8px' }}>
<ToggleButton icon={CropOrginalSvg} title='Auto-crop' inline isSelected={cropParams.auto}

View File

@@ -22,8 +22,6 @@ import { ViewportHelpContent } from './help';
export class SimpleSettingsControl extends PluginUIComponent {
componentDidMount() {
if (!this.plugin.canvas3d) return;
this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
this.subscribe(this.plugin.canvas3d!.camera.stateChanged, state => {

View File

@@ -1,8 +1,7 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Adam Midlik <midlik@gmail.com>
*/
import { PluginStateTransform, PluginStateObject } from '../../mol-plugin-state/objects';
@@ -40,7 +39,7 @@ namespace PluginBehavior {
'misc': 'Miscellaneous'
};
export interface CreateParams<P extends {}> {
export interface CreateParams<P> {
name: string,
category: keyof typeof Categories,
ctor: Ctor<P>,
@@ -73,7 +72,7 @@ namespace PluginBehavior {
return categoryMap.get(t.id)!;
}
export function create<P extends {}>(params: CreateParams<P>) {
export function create<P>(params: CreateParams<P>) {
const t = PluginStateTransform.CreateBuiltIn<Category, Behavior, P>({
name: params.name,
display: params.display,
@@ -113,7 +112,7 @@ namespace PluginBehavior {
};
}
export abstract class Handler<P extends {} = {}> implements PluginBehavior<P> {
export abstract class Handler<P = { }> implements PluginBehavior<P> {
private subs: PluginCommand.Subscription[] = [];
protected subscribeCommand<T>(cmd: PluginCommand<T>, action: PluginCommand.Action<T>) {
this.subs.push(cmd.subscribe(this.ctx, action));
@@ -145,18 +144,8 @@ namespace PluginBehavior {
protected subscribeCommand<T>(cmd: PluginCommand<T>, action: PluginCommand.Action<T>) {
this.subs.push(cmd.subscribe(this.plugin, action));
}
protected subscribeObservable<T>(o: Observable<T>, action: (v: T) => void): PluginCommand.Subscription {
const sub = o.subscribe(action);
this.subs.push(sub);
return {
unsubscribe: () => {
const idx = this.subs.indexOf(sub);
if (idx >= 0) {
this.subs.splice(idx, 1);
sub.unsubscribe();
}
}
};
protected subscribeObservable<T>(o: Observable<T>, action: (v: T) => void) {
this.subs.push(o.subscribe(action));
}
dispose(): void {
for (const s of this.subs) s.unsubscribe();

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 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>
* @author Adam Midlik <midlik@gmail.com>
*/
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
@@ -25,10 +24,6 @@ import { PluginContext } from '../../../context';
import { EmptyLoci, Loci, isEmptyLoci } from '../../../../mol-model/loci';
import { Asset } from '../../../../mol-util/assets';
import { GlobalModelTransformInfo } from '../../../../mol-model/structure/model/properties/global-transform';
import { distinctUntilChanged, filter, map, Observable, throttleTime } from 'rxjs';
import { Camera } from '../../../../mol-canvas3d/camera';
import { PluginCommand } from '../../../command';
import { SingleAsyncQueue } from '../../../../mol-util/single-async-queue';
export class VolumeStreaming extends PluginStateObject.CreateBehavior<VolumeStreaming.Behavior>({ name: 'Volume Streaming' }) { }
@@ -58,7 +53,7 @@ export namespace VolumeStreaming {
valuesInfo: [{ mean: 0, min: -1, max: 1, sigma: 0.1 }, { mean: 0, min: -1, max: 1, sigma: 0.1 }]
};
export function createParams(options: { data?: VolumeServerInfo.Data, defaultView?: ViewTypes, channelParams?: DefaultChannelParams } = {}) {
export function createParams(options: { data?: VolumeServerInfo.Data, defaultView?: ViewTypes, channelParams?: DefaultChannelParams } = { }) {
const { data, defaultView, channelParams } = options;
const map = new Map<string, VolumeServerInfo.EntryData>();
if (data) data.entries.forEach(d => map.set(d.dataId, d));
@@ -73,7 +68,7 @@ export namespace VolumeStreaming {
export type EntryParams = PD.Values<EntryParamDefinition>
export function createEntryParams(options: { entryData?: VolumeServerInfo.EntryData, defaultView?: ViewTypes, structure?: Structure, channelParams?: DefaultChannelParams }) {
const { entryData, defaultView, structure, channelParams = {} } = options;
const { entryData, defaultView, structure, channelParams = { } } = options;
// fake the info
const info = entryData || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: Volume.IsoValue.relative(0) };
@@ -91,24 +86,19 @@ export namespace VolumeStreaming {
bottomLeft: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }),
topRight: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }),
}, { description: 'Box around focused element.', isFlat: true }),
'camera-target': PD.Group({
radius: PD.Numeric(0.5, { min: 0, max: 1, step: 0.05 }, { description: 'Radius within which the volume is shown (relative to the field of view).' }),
// Minimal detail level for the inside of the zoomed region (real detail can be higher, depending on the region size)
dynamicDetailLevel: createDetailParams(info.header.availablePrecisions, 0, { label: 'Dynamic Detail' }),
bottomLeft: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }),
topRight: PD.Vec3(Vec3.create(0, 0, 0), {}, { isHidden: true }),
}, { description: 'Box around camera target.', isFlat: true }),
'cell': PD.Group<{}>({}),
// Show selection-box if available and cell otherwise.
'auto': PD.Group({
radius: PD.Numeric(5, { min: 0, max: 50, step: 0.5 }, { description: 'Radius in \u212B within which the volume is shown.' }),
selectionDetailLevel: createDetailParams(info.header.availablePrecisions, 6, { label: 'Selection Detail' }),
selectionDetailLevel: PD.Select<number>(Math.min(6, info.header.availablePrecisions.length - 1),
info.header.availablePrecisions.map((p, i) => [i, `${i + 1} [ ${Math.pow(p.maxVoxels, 1 / 3) | 0}^3 cells ]`] as [number, string]), { label: 'Selection Detail', description: 'Determines the maximum number of voxels. Depending on the size of the volume options are in the range from 0 (0.52M voxels) to 6 (25.17M voxels).' }),
isSelection: PD.Boolean(false, { isHidden: true }),
bottomLeft: PD.Vec3(box.min, {}, { isHidden: true }),
topRight: PD.Vec3(box.max, {}, { isHidden: true }),
}, { description: 'Box around focused element.', isFlat: true })
}, { options: ViewTypeOptions, description: 'Controls what of the volume is displayed. "Off" hides the volume alltogether. "Bounded box" shows the volume inside the given box. "Around Interaction" shows the volume around the focused element/atom. "Whole Structure" shows the volume for the whole structure.' }),
detailLevel: createDetailParams(info.header.availablePrecisions, 3),
detailLevel: PD.Select<number>(Math.min(3, info.header.availablePrecisions.length - 1),
info.header.availablePrecisions.map((p, i) => [i, `${i + 1} [ ${Math.pow(p.maxVoxels, 1 / 3) | 0}^3 cells ]`] as [number, string]), { description: 'Determines the maximum number of voxels. Depending on the size of the volume options are in the range from 0 (0.52M voxels) to 6 (25.17M voxels).' }),
channels: info.kind === 'em'
? PD.Group({
'em': channelParam('EM', Color(0x638F8F), info.emDefaultContourLevel || Volume.IsoValue.relative(1), info.header.sampling[0].valuesInfo[0], channelParams['em'])
@@ -121,40 +111,13 @@ export namespace VolumeStreaming {
};
}
function createDetailParams(availablePrecisions: VolumeServerHeader.DetailLevel[], preferredPrecision: number, info?: PD.Info) {
return PD.Select<number>(Math.min(preferredPrecision, availablePrecisions.length - 1),
availablePrecisions.map((p, i) => [i, `${i + 1} [ ${Math.pow(p.maxVoxels, 1 / 3) | 0}^3 cells ]`] as [number, string]),
{
description: 'Determines the maximum number of voxels. Depending on the size of the volume options are in the range from 1 (0.52M voxels) to 7 (25.17M voxels).',
...info
}
);
}
export const ViewTypeOptions = [['off', 'Off'], ['box', 'Bounded Box'], ['selection-box', 'Around Focus'], ['cell', 'Whole Structure'], ['auto', 'Auto']] as [ViewTypes, string][];
export function copyParams(origParams: Params): Params {
return {
entry: {
name: origParams.entry.name,
params: {
detailLevel: origParams.entry.params.detailLevel,
channels: origParams.entry.params.channels,
view: {
name: origParams.entry.params.view.name,
params: { ...origParams.entry.params.view.params } as any,
}
}
}
};
}
export const ViewTypeOptions = [['off', 'Off'], ['box', 'Bounded Box'], ['selection-box', 'Around Focus'], ['camera-target', 'Around Camera'], ['cell', 'Whole Structure'], ['auto', 'Auto']] as [ViewTypes, string][];
export type ViewTypes = 'off' | 'box' | 'selection-box' | 'camera-target' | 'cell' | 'auto'
export type ViewTypes = 'off' | 'box' | 'selection-box' | 'cell' | 'auto'
export type ParamDefinition = ReturnType<typeof createParams>
export type Params = PD.Values<ParamDefinition>
type ChannelsInfo = { [name in ChannelType]?: { isoValue: Volume.IsoValue, color: Color, wireframe: boolean, opacity: number } }
type ChannelsData = { [name in 'EM' | '2FO-FC' | 'FO-FC']?: Volume }
@@ -177,14 +140,6 @@ export namespace VolumeStreaming {
private lastLoci: StructureElement.Loci | EmptyLoci = EmptyLoci;
private ref: string = '';
public infoMap: Map<string, VolumeServerInfo.EntryData>;
private updateQueue: SingleAsyncQueue;
private cameraTargetObservable = this.plugin.canvas3d!.didDraw!.pipe(
throttleTime(500, undefined, { 'leading': true, 'trailing': true }),
map(() => this.plugin.canvas3d?.camera.getSnapshot()),
distinctUntilChanged((a, b) => this.isCameraTargetSame(a, b)),
filter(a => a !== undefined),
) as Observable<Camera.Snapshot>;
private cameraTargetSubscription?: PluginCommand.Subscription = undefined;
channels: Channels = {};
@@ -208,9 +163,6 @@ export namespace VolumeStreaming {
if (this.params.entry.params.view.name === 'auto' && this.params.entry.params.view.params.isSelection) {
detail = this.params.entry.params.view.params.selectionDetailLevel;
}
if (this.params.entry.params.view.name === 'camera-target' && box) {
detail = this.decideDetail(box, this.params.entry.params.view.params.dynamicDetailLevel);
}
url += `?detail=${detail}`;
@@ -249,21 +201,58 @@ export namespace VolumeStreaming {
return ret;
}
private async updateParams(box: Box3D | undefined, autoIsSelection: boolean = false) {
const newParams = copyParams(this.params);
const viewType = newParams.entry.params.view.name;
if (viewType !== 'off' && viewType !== 'cell') {
newParams.entry.params.view.params.bottomLeft = box?.min || Vec3.zero();
newParams.entry.params.view.params.topRight = box?.max || Vec3.zero();
}
if (viewType === 'auto') {
newParams.entry.params.view.params.isSelection = autoIsSelection;
}
private updateSelectionBoxParams(box: Box3D) {
if (this.params.entry.params.view.name !== 'selection-box') return;
const state = this.plugin.state.data;
const newParams: Params = {
...this.params,
entry: {
name: this.params.entry.name,
params: {
...this.params.entry.params,
view: {
name: 'selection-box' as const,
params: {
radius: this.params.entry.params.view.params.radius,
bottomLeft: box.min,
topRight: box.max
}
}
}
}
};
const update = state.build().to(this.ref).update(newParams);
await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
}
private updateAutoParams(box: Box3D | undefined, isSelection: boolean) {
if (this.params.entry.params.view.name !== 'auto') return;
const state = this.plugin.state.data;
const newParams: Params = {
...this.params,
entry: {
name: this.params.entry.name,
params: {
...this.params.entry.params,
view: {
name: 'auto' as const,
params: {
radius: this.params.entry.params.view.params.radius,
selectionDetailLevel: this.params.entry.params.view.params.selectionDetailLevel,
isSelection,
bottomLeft: box?.min || Vec3.zero(),
topRight: box?.max || Vec3.zero()
}
}
}
}
};
const update = state.build().to(this.ref).update(newParams);
PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
}
private getStructureRoot() {
@@ -314,18 +303,6 @@ export namespace VolumeStreaming {
}
}
private isCameraTargetSame(a?: Camera.Snapshot, b?: Camera.Snapshot): boolean {
if (!a || !b) return false;
const targetSame = Vec3.equals(a.target, b.target);
const sqDistA = Vec3.squaredDistance(a.target, a.position);
const sqDistB = Vec3.squaredDistance(b.target, b.position);
const distanceSame = Math.abs(sqDistA - sqDistB) / sqDistA < 1e-3;
return targetSame && distanceSame;
}
private cameraTargetDistance(snapshot: Camera.Snapshot): number {
return Vec3.distance(snapshot.target, snapshot.position);
}
private _invTransform: Mat4 = Mat4();
private getBoxFromLoci(loci: StructureElement.Loci | EmptyLoci): Box3D {
if (Loci.isEmpty(loci) || isEmptyLoci(loci)) {
@@ -351,82 +328,39 @@ export namespace VolumeStreaming {
}
private updateAuto(loci: StructureElement.Loci | EmptyLoci) {
this.updateQueue.enqueue(async () => {
this.lastLoci = loci;
if (isEmptyLoci(loci)) {
await this.updateParams(this.info.kind === 'x-ray' ? this.data.structure.boundary.box : void 0, false);
} else {
await this.updateParams(this.getBoxFromLoci(loci), true);
}
});
// if (Loci.areEqual(this.lastLoci, loci)) {
// this.lastLoci = EmptyLoci;
// this.updateSelectionBoxParams(Box3D.empty());
// return;
// }
this.lastLoci = loci;
if (isEmptyLoci(loci)) {
this.updateAutoParams(this.info.kind === 'x-ray' ? this.data.structure.boundary.box : void 0, false);
return;
}
const box = this.getBoxFromLoci(loci);
this.updateAutoParams(box, true);
}
private updateSelectionBox(loci: StructureElement.Loci | EmptyLoci) {
this.updateQueue.enqueue(async () => {
if (Loci.areEqual(this.lastLoci, loci)) {
this.lastLoci = EmptyLoci;
} else {
this.lastLoci = loci;
}
const box = this.getBoxFromLoci(this.lastLoci);
await this.updateParams(box);
});
}
private updateCameraTarget(snapshot: Camera.Snapshot) {
this.updateQueue.enqueue(async () => {
const origManualReset = this.plugin.canvas3d?.props.camera.manualReset;
try {
if (!origManualReset) this.plugin.canvas3d?.setProps({ camera: { manualReset: true } });
const box = this.boxFromCameraTarget(snapshot, true);
await this.updateParams(box);
} finally {
if (!origManualReset) this.plugin.canvas3d?.setProps({ camera: { manualReset: origManualReset } });
}
});
}
private boxFromCameraTarget(snapshot: Camera.Snapshot, boundByBoundarySize: boolean): Box3D {
const target = snapshot.target;
const distance = this.cameraTargetDistance(snapshot);
const top = Math.tan(0.5 * snapshot.fov) * distance;
let radius = top;
const viewport = this.plugin.canvas3d?.camera.viewport;
if (viewport && viewport.width > viewport.height) {
radius *= viewport.width / viewport.height;
if (Loci.areEqual(this.lastLoci, loci)) {
this.lastLoci = EmptyLoci;
this.updateSelectionBoxParams(Box3D());
return;
}
const relativeRadius = this.params.entry.params.view.name === 'camera-target' ? this.params.entry.params.view.params.radius : 0.5;
radius *= relativeRadius;
let radiusX, radiusY, radiusZ;
if (boundByBoundarySize) {
const bBoxSize = Vec3.zero();
Box3D.size(bBoxSize, this.data.structure.boundary.box);
radiusX = Math.min(radius, 0.5 * bBoxSize[0]);
radiusY = Math.min(radius, 0.5 * bBoxSize[1]);
radiusZ = Math.min(radius, 0.5 * bBoxSize[2]);
} else {
radiusX = radiusY = radiusZ = radius;
}
return Box3D.create(
Vec3.create(target[0] - radiusX, target[1] - radiusY, target[2] - radiusZ),
Vec3.create(target[0] + radiusX, target[1] + radiusY, target[2] + radiusZ)
);
}
private decideDetail(box: Box3D, baseDetail: number): number {
const cellVolume = this.info.kind === 'x-ray'
? Box3D.volume(this.data.structure.boundary.box)
: this.info.header.spacegroup.size.reduce((a, b) => a * b, 1);
const boxVolume = Box3D.volume(box);
let ratio = boxVolume / cellVolume;
const maxDetail = this.info.header.availablePrecisions.length - 1;
let detail = baseDetail;
while (ratio <= 0.5 && detail < maxDetail) {
ratio *= 2;
detail += 1;
this.lastLoci = loci;
if (isEmptyLoci(loci)) {
this.updateSelectionBoxParams(Box3D());
return;
}
// console.log(`Decided dynamic detail: ${detail}, (base detail: ${baseDetail}, box/cell volume ratio: ${boxVolume / cellVolume})`);
return detail;
const box = this.getBoxFromLoci(loci);
this.updateSelectionBoxParams(box);
}
async update(params: Params) {
@@ -435,11 +369,6 @@ export namespace VolumeStreaming {
this.params = params;
let box: Box3D | undefined = void 0, emptyData = false;
if (params.entry.params.view.name !== 'camera-target' && this.cameraTargetSubscription) {
this.cameraTargetSubscription.unsubscribe();
this.cameraTargetSubscription = undefined;
}
switch (params.entry.params.view.name) {
case 'off':
emptyData = true;
@@ -459,12 +388,6 @@ export namespace VolumeStreaming {
Box3D.expand(box, box, Vec3.create(r, r, r));
break;
}
case 'camera-target':
if (!this.cameraTargetSubscription) {
this.cameraTargetSubscription = this.subscribeObservable(this.cameraTargetObservable, (e) => this.updateCameraTarget(e));
}
box = this.boxFromCameraTarget(this.plugin.canvas3d!.camera.getSnapshot(), true);
break;
case 'cell':
box = this.info.kind === 'x-ray'
? this.data.structure.boundary.box
@@ -516,7 +439,6 @@ export namespace VolumeStreaming {
getDescription() {
if (this.params.entry.params.view.name === 'selection-box') return 'Selection';
if (this.params.entry.params.view.name === 'camera-target') return 'Camera';
if (this.params.entry.params.view.name === 'box') return 'Static Box';
if (this.params.entry.params.view.name === 'cell') return 'Cell';
return '';
@@ -527,7 +449,6 @@ export namespace VolumeStreaming {
this.infoMap = new Map<string, VolumeServerInfo.EntryData>();
this.data.entries.forEach(info => this.infoMap.set(info.dataId, info));
this.updateQueue = new SingleAsyncQueue();
}
}
}
}

View File

@@ -1,9 +1,8 @@
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 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>
* @author Adam Midlik <midlik@gmail.com>
*/
import { PluginStateObject as SO, PluginStateTransform } from '../../../../mol-plugin-state/objects';
@@ -220,7 +219,6 @@ const CreateVolumeStreamingBehavior = PluginStateTransform.BuiltIn({
canAutoUpdate: ({ oldParams, newParams }) => {
return oldParams.entry.params.view === newParams.entry.params.view
|| newParams.entry.params.view.name === 'selection-box'
|| newParams.entry.params.view.name === 'camera-target'
|| newParams.entry.params.view.name === 'off';
},
apply: ({ a, params }, plugin: PluginContext) => Task.create('Volume streaming', async _ => {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 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>
@@ -119,9 +119,7 @@ export function Highlight(ctx: PluginContext) {
ctx.managers.interactivity.lociHighlights.highlight({ loci: Structure.Loci(cell.obj.data) }, false);
} else if (cell && SO.isRepresentation3D(cell.obj)) {
const { repr } = cell.obj.data;
for (const loci of repr.getAllLoci()) {
ctx.managers.interactivity.lociHighlights.highlight({ loci, repr }, false);
}
ctx.managers.interactivity.lociHighlights.highlight({ loci: repr.getLoci(), repr }, false);
} else if (SO.Molecule.Structure.Selections.is(cell.obj)) {
for (const entry of cell.obj.data) {
ctx.managers.interactivity.lociHighlights.highlight({ loci: entry.loci }, false);

View File

@@ -31,11 +31,10 @@ export const PluginConfig = {
PixelScale: item('plugin-config.pixel-scale', 1),
PickScale: item('plugin-config.pick-scale', 0.25),
PickPadding: item('plugin-config.pick-padding', 3),
EnableWboit: item('plugin-config.enable-wboit', true),
EnableWboit: item('plugin-config.enable-wboit', PluginFeatureDetection.wboit),
// as of Oct 1 2021, WebGL 2 doesn't work on iOS 15.
// TODO: check back in a few weeks to see if it was fixed
PreferWebGl1: item('plugin-config.prefer-webgl1', PluginFeatureDetection.preferWebGl1),
AllowMajorPerformanceCaveat: item('plugin-config.allow-major-performance-caveat', false),
},
State: {
DefaultServer: item('plugin-state.server', 'https://webchem.ncbr.muni.cz/molstar-state'),

View File

@@ -201,8 +201,7 @@ export class PluginContext {
const pickPadding = this.config.get(PluginConfig.General.PickPadding) ?? 1;
const enableWboit = this.config.get(PluginConfig.General.EnableWboit) || false;
const preferWebGl1 = this.config.get(PluginConfig.General.PreferWebGl1) || false;
const failIfMajorPerformanceCaveat = !(this.config.get(PluginConfig.General.AllowMajorPerformanceCaveat) ?? false);
(this.canvas3dContext as Canvas3DContext) = Canvas3DContext.fromCanvas(canvas, this.managers.asset, { antialias, preserveDrawingBuffer, pixelScale, pickScale, pickPadding, enableWboit, preferWebGl1, failIfMajorPerformanceCaveat });
(this.canvas3dContext as Canvas3DContext) = Canvas3DContext.fromCanvas(canvas, this.managers.asset, { antialias, preserveDrawingBuffer, pixelScale, pickScale, pickPadding, enableWboit, preferWebGl1 });
}
(this.canvas3d as Canvas3D) = Canvas3D.create(this.canvas3dContext!);
this.canvas3dInit.next(true);

View File

@@ -29,4 +29,11 @@ export const PluginFeatureDetection = {
const isTouchScreen = navigator.maxTouchPoints >= 4; // true for iOS 13 (and hopefully beyond)
return !(window as any).MSStream && (isIOS || (isAppleDevice && isTouchScreen));
},
get wboit() {
return true; // for testing
// if (typeof navigator === 'undefined' || typeof window === 'undefined') return true;
// // disable Wboit in Safari 15
// return !/Version\/15.\d Safari/.test(navigator.userAgent);
}
};

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -154,8 +154,8 @@ interface Representation<D, P extends PD.Params = {}, S extends Representation.S
createOrUpdate: (props?: Partial<PD.Values<P>>, data?: D) => Task<void>
setState: (state: Partial<S>) => void
setTheme: (theme: Theme) => void
getLoci: (pickingId: PickingId) => ModelLoci
getAllLoci: () => ModelLoci[]
/** If no pickingId is given, returns a Loci for the whole representation */
getLoci: (pickingId?: PickingId) => ModelLoci
mark: (loci: ModelLoci, action: MarkerAction) => boolean
destroy: () => void
}
@@ -227,7 +227,6 @@ namespace Representation {
setState: () => {},
setTheme: () => {},
getLoci: () => EmptyLoci,
getAllLoci: () => [],
mark: () => false,
destroy: () => {}
};
@@ -328,7 +327,7 @@ namespace Representation {
},
get state() { return currentState; },
get theme() { return currentTheme; },
getLoci: (pickingId: PickingId) => {
getLoci: (pickingId?: PickingId) => {
const { visuals } = currentProps;
for (let i = 0, il = reprList.length; i < il; ++i) {
if (!visuals || visuals.includes(reprMap[i])) {
@@ -338,16 +337,6 @@ namespace Representation {
}
return EmptyLoci;
},
getAllLoci: () => {
const loci: ModelLoci[] = [];
const { visuals } = currentProps;
for (let i = 0, il = reprList.length; i < il; ++i) {
if (!visuals || visuals.includes(reprMap[i])) {
loci.push(...reprList[i].getAllLoci());
}
}
return loci;
},
mark: (loci: ModelLoci, action: MarkerAction) => {
let marked = false;
for (let i = 0, il = reprList.length; i < il; ++i) {
@@ -410,10 +399,6 @@ namespace Representation {
// TODO
return EmptyLoci;
},
getAllLoci: () => {
// TODO
return [];
},
mark: (loci: ModelLoci, action: MarkerAction) => {
// TODO
return false;

View File

@@ -213,16 +213,14 @@ export function ShapeRepresentation<D, G extends Geometry, P extends Geometry.Pa
get geometryVersion() { return geometryVersion; },
updated,
createOrUpdate,
getLoci(pickingId: PickingId) {
getLoci(pickingId?: PickingId) {
if (pickingId === undefined) return Shape.Loci(_shape);
const { objectId, groupId, instanceId } = pickingId;
if (_renderObject && _renderObject.id === objectId) {
return ShapeGroup.Loci(_shape, [{ ids: OrderedSet.ofSingleton(groupId), instance: instanceId }]);
}
return EmptyLoci;
},
getAllLoci() {
return [Shape.Loci(_shape)];
},
mark(loci: Loci, action: MarkerAction) {
if (!MarkerActions.is(_state.markerActions, action)) return false;
if (ShapeGroup.isLoci(loci) || Shape.isLoci(loci)) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -72,14 +72,11 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
});
}
function getLoci(pickingId: PickingId) {
function getLoci(pickingId?: PickingId) {
if (pickingId === undefined) return Structure.Loci(_structure.target);
return visual ? visual.getLoci(pickingId) : EmptyLoci;
}
function getAllLoci() {
return [Structure.Loci(_structure.target)];
}
function mark(loci: Loci, action: MarkerAction) {
if (!_structure) return false;
if (!MarkerActions.is(_state.markerActions, action)) return false;
@@ -160,7 +157,6 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
setState,
setTheme,
getLoci,
getAllLoci,
mark,
destroy
};

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -185,7 +185,8 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
});
}
function getLoci(pickingId: PickingId) {
function getLoci(pickingId?: PickingId) {
if (pickingId === undefined) return Structure.Loci(_structure.target);
let loci: Loci = EmptyLoci;
visuals.forEach(({ visual }) => {
const _loci = visual.getLoci(pickingId);
@@ -194,10 +195,6 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
return loci;
}
function getAllLoci() {
return [Structure.Loci(_structure.target)];
}
function mark(loci: Loci, action: MarkerAction) {
if (!_structure) return false;
if (!MarkerActions.is(_state.markerActions, action)) return false;
@@ -305,7 +302,6 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
setState,
setTheme,
getLoci,
getAllLoci,
mark,
destroy
};

View File

@@ -358,12 +358,10 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx:
createOrUpdate,
setState,
setTheme,
getLoci: (pickingId: PickingId): Loci => {
getLoci: (pickingId?: PickingId): Loci => {
if (pickingId === undefined) return getLoci(_volume, _props);
return visual ? visual.getLoci(pickingId) : EmptyLoci;
},
getAllLoci: (): Loci[] => {
return [getLoci(_volume, _props)];
},
mark,
destroy
};

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 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>
@@ -333,7 +333,6 @@ const bondProperty = {
flags: bondProp(Types.BondFlags),
order: bondProp(Type.Num),
key: bondProp(Type.Num),
length: bondProp(Type.Num),
atomA: bondProp(Types.ElementReference),
atomB: bondProp(Types.ElementReference)
@@ -357,5 +356,5 @@ export const structureQuery = {
combinator,
atomSet,
atomProperty,
bondProperty
bondProperty: bondProperty
};

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 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>
@@ -211,21 +211,21 @@ const symbols = [
// ============= FILTERS ================
D(MolScript.structureQuery.filter.pick, (ctx, xs) => Queries.filters.pick(xs[0] as any, xs['test'])(ctx)),
D(MolScript.structureQuery.filter.first, (ctx, xs) => Queries.filters.first(xs[0] as any)(ctx)),
D(MolScript.structureQuery.filter.withSameAtomProperties, (ctx, xs) => Queries.filters.withSameAtomProperties(xs[0] as any, xs['source'] as any, xs['property'])(ctx)),
D(MolScript.structureQuery.filter.withSameAtomProperties, (ctx, xs) => Queries.filters.withSameAtomProperties(xs[0] as any, xs['source'] as any, xs['property'] as any)(ctx)),
D(MolScript.structureQuery.filter.intersectedBy, (ctx, xs) => Queries.filters.areIntersectedBy(xs[0] as any, xs['by'] as any)(ctx)),
D(MolScript.structureQuery.filter.within, (ctx, xs) => Queries.filters.within({
query: xs[0] as any,
target: xs['target'] as any,
minRadius: xs['min-radius']?.(ctx) as any,
maxRadius: xs['max-radius']?.(ctx) as any,
minRadius: xs['min-radius'] as any,
maxRadius: xs['max-radius'] as any,
elementRadius: xs['atom-radius'] as any,
invert: xs['invert']?.(ctx) as any
invert: xs['invert'] as any
})(ctx)),
D(MolScript.structureQuery.filter.isConnectedTo, (ctx, xs) => Queries.filters.isConnectedTo({
query: xs[0] as any,
target: xs['target'] as any,
disjunct: xs['disjunct']?.(ctx) as any,
invert: xs['invert']?.(ctx) as any,
disjunct: xs['disjunct'] as any,
invert: xs['invert'] as any,
bondTest: xs['bond-test']
})(ctx)),
@@ -248,9 +248,6 @@ const symbols = [
D(MolScript.structureQuery.generator.rings, function structureQuery_generator_rings(ctx, xs) {
return Queries.generators.rings(xs?.['fingerprint']?.(ctx) as any, xs?.['only-aromatic']?.(ctx))(ctx);
}),
D(MolScript.structureQuery.generator.queryInSelection, function structureQuery_generator_queryInSelection(ctx, xs) {
return Queries.generators.querySelection(xs[0] as any, xs['query'] as any, xs['in-complement']?.(ctx) as any)(ctx);
}),
// ============= MODIFIERS ================
@@ -281,7 +278,6 @@ const symbols = [
fixedPoint: xs['fixed-point']?.(ctx) ?? false
})(ctx);
}),
D(MolScript.structureQuery.modifier.intersectBy, function structureQuery_modifier_intersectBy(ctx, xs) { return Queries.modifiers.intersectBy(xs[0] as any, xs['by'] as any)(ctx); }),
// ============= COMBINATORS ================
@@ -357,27 +353,9 @@ const symbols = [
D(MolScript.structureQuery.atomProperty.macromolecular.secondaryStructureFlags, atomProp(StructureProperties.residue.secondary_structure_type)),
D(MolScript.structureQuery.atomProperty.macromolecular.chemCompType, atomProp(StructureProperties.residue.chem_comp_type)),
// ============= ATOM SET ================
D(MolScript.structureQuery.atomSet.atomCount,
function structureQuery_atomset_atomCount(ctx, xs) {
return Queries.atomset.atomCount(ctx);
}),
D(MolScript.structureQuery.atomSet.countQuery,
function structureQuery_atomset_countQuery(ctx, xs) {
return Queries.atomset.countQuery(xs[0] as any)(ctx);
}),
D(MolScript.structureQuery.atomSet.propertySet,
function structureQuery_atomset_propertySet(ctx, xs) {
return Queries.atomset.propertySet(xs[0] as any)(ctx);
}),
// ============= BOND PROPERTIES ================
D(MolScript.structureQuery.bondProperty.order, (ctx, xs) => ctx.atomicBond.order),
D(MolScript.structureQuery.bondProperty.flags, (ctx, xs) => ctx.atomicBond.type),
D(MolScript.structureQuery.bondProperty.key, (ctx, xs) => ctx.atomicBond.key),
D(MolScript.structureQuery.bondProperty.atomA, (ctx, xs) => ctx.atomicBond.a),
D(MolScript.structureQuery.bondProperty.atomB, (ctx, xs) => ctx.atomicBond.b),
D(MolScript.structureQuery.bondProperty.length, (ctx, xs) => ctx.atomicBond.length),
@@ -428,4 +406,4 @@ function getArray<T = any>(ctx: QueryContext, xs: any): T[] {
for (const s of symbols) {
DefaultQueryRuntimeTable.addSymbol(s);
}
})();
})();

View File

@@ -6,12 +6,10 @@
import { transpileMolScript } from './script/mol-script/symbols';
import { parseMolScript } from './language/parser';
import { parse } from './transpile';
import { Expression } from './language/expression';
import { StructureElement, QueryContext, StructureSelection, Structure, QueryFn, QueryContextOptions } from '../mol-model/structure';
import { compile } from './runtime/query/compiler';
import { MolScriptBuilder } from './language/builder';
import { assertUnreachable } from '../mol-util/type-helpers';
export { Script };
@@ -22,13 +20,7 @@ function Script(expression: string, language: Script.Language): Script {
}
namespace Script {
export const Info = {
'mol-script': 'Mol-Script',
'pymol': 'PyMOL',
'vmd': 'VMD',
'jmol': 'Jmol',
};
export type Language = keyof typeof Info;
export type Language = 'mol-script'
export function is(x: any): x is Script {
return !!x && typeof (x as Script).expression === 'string' && !!(x as Script).language;
@@ -44,13 +36,8 @@ namespace Script {
const parsed = parseMolScript(script.expression);
if (parsed.length === 0) throw new Error('No query');
return transpileMolScript(parsed[0]);
case 'pymol':
case 'jmol':
case 'vmd':
return parse(script.language, script.expression);
default:
assertUnreachable(script.language);
}
throw new Error('unsupported script language');
}
export function toQuery(script: Script): QueryFn<StructureSelection> {
@@ -69,4 +56,4 @@ namespace Script {
const query = compile<StructureSelection>(e);
return query(new QueryContext(structure, options));
}
}
}

View File

@@ -1,27 +0,0 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*
* Adapted from MolQL src/transpile.ts
*/
import { Transpiler } from './transpilers/transpiler';
import { _transpiler } from './transpilers/all';
import { Expression } from './language/expression';
import { Script } from './script';
const transpiler: {[index: string]: Transpiler} = _transpiler;
export function parse(lang: Script.Language, str: string): Expression {
try {
const query = transpiler[lang](str);
return query;
} catch (e) {
console.error(e.message);
throw e;
}
}

View File

@@ -1,26 +0,0 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
* Adapted from MolQL project
**/
import { Transpiler } from '../transpiler';
import { _transpiler as transpilers } from '../all';
function testTranspilerExamples(name: string, transpiler: Transpiler) {
describe(`${name} examples`, () => {
const examples = require(`../${name}/examples`).examples;
// console.log(examples);
for (const e of examples) {
it(e.name, () => {
// check if it transpiles and compiles/typechecks.
transpiler(e.value);
});
}
});
}
testTranspilerExamples('pymol', transpilers.pymol);
testTranspilerExamples('vmd', transpilers.vmd);
testTranspilerExamples('jmol', transpilers.jmol);

View File

@@ -1,115 +0,0 @@
/**
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
* Adapted from MolQL project
*/
import * as u from './utils';
import { transpiler } from '../jmol/parser';
import { keywords } from '../jmol/keywords';
import { properties } from '../jmol/properties';
import { operators } from '../jmol/operators';
const general = {
supported: [
// atom expressions
'123',
'-42',
'_C',
'.CA',
'ALA',
'%A',
'^B',
':C',
'/2',
'10^A:F.CA%C/0',
'10^A:F.CA%C',
'10^A:F.CA',
'10^A:F',
'10^A',
'10:F.CA',
'10/0',
'32 or 42',
'.CA/0 OR 42:A',
'!23',
'not ASP',
'(ASP or .CA)',
'ASP and .CA',
'123.CA',
'(1 or 2) and .CA',
'(1 or 2) and (.CA or .N)',
'.CA and (2 or 3)',
'.CA and (2 or 3) and ^A',
'!32 or :A and .CA',
// trimming
' atomName = CA ',
'atomName = CA ',
' atomName = CA',
// value comparison
'resno > 10',
// atom expression
'[LEU]100:A.CA',
'[LEU]100:A',
'[LEU]100.CA',
'[LEU]:A.CA',
'[LEU].CA',
// comma as OR
'100, 42, ALA',
// residue numbering
'(1-10,15,21-30)',
// within
'within(5,[HEM])',
// within with parentheses
'(within(5,[HEM])) and backbone',
'( within(5,[HEM]) ) and backbone',
// trimming
'[ALA] and [VAL] ',
' [ALA] and [VAL] ',
' [ALA] and [VAL]',
// within with whitespaces
'within ( 5 , [HEM] ) ',
// un-braketed residue name
'LEU and ILE',
// un-parenthesized residue index range
'100-120,220',
// un-parenthesized residue index
'20',
// within in the head or the middle of sentence
'within ( 5 , [HEM] ) and backbone',
// atom expressions with ranges
'19-32:A',
'-2-32:B',
'-10--2:C',
'[1FO]19-32:A',
],
unsupported: [
// values outside of comparisons
'foobar',
'protein or foobar',
]
};
describe('jmol general', () => {
general.supported.forEach(str => {
it(str, () => {
transpiler(str);
});
});
general.unsupported.forEach(str => {
it(str, () => {
const transpileStr = () => transpiler(str);
expect(transpileStr).toThrow();
expect(transpileStr).not.toThrowError(RangeError);
});
});
});
describe('jmol keywords', () => u.testKeywords(keywords, transpiler));
describe('jmol properties', () => u.testProperties(properties, transpiler));
describe('jmol operators', () => u.testOperators(operators, transpiler));

View File

@@ -1,73 +0,0 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*/
import * as u from './utils';
import { transpiler } from '../pymol/parser';
import { keywords } from '../pymol/keywords';
import { properties } from '../pymol/properties';
import { operators } from '../pymol/operators';
const general = {
supported: [
// macros
'10/cb',
'a/10-12/ca',
'lig/b/6+8/c+o',
// trimming
' name CA ',
'name CA ',
' name CA',
],
unsupported: [
// macros
'pept/enz/c/3/n',
'pept/enz///n',
'/pept/lig/',
'/pept/lig/a',
'/pept/lig/a/10',
'/pept/lig/a/10/ca',
'/pept//a/10',
// object
'foobar',
'protein and bazbar',
]
};
describe('pymol general', () => {
general.supported.forEach(str => {
it(str, () => {
transpiler(str);
// compile(expr);
});
});
general.unsupported.forEach(str => {
it(str, () => {
const transpileStr = () => transpiler(str);
expect(transpileStr).toThrow();
expect(transpileStr).not.toThrowError(RangeError);
});
});
});
// check against builder output
// 'not (resi 42 or chain A)'
// '!resi 42 or chain A'
// 'b >= 0.3',
// 'b != 0.3',
// 'b>0.3',
// 'b <0.3',
// 'b <= 0.3',
// 'b = 1',
// 'fc.=.1',
describe('pymol keywords', () => u.testKeywords(keywords, transpiler));
describe('pymol operators', () => u.testOperators(operators, transpiler));
describe('pymol properties', () => u.testProperties(properties, transpiler));

View File

@@ -1,69 +0,0 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panangiot_tourlov@hotmail.com>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*/
import { Transpiler } from '../transpiler';
import { KeywordDict, PropertyDict, OperatorList } from '../types';
export function testKeywords(keywords: KeywordDict, transpiler: Transpiler) {
for (const name in keywords) {
it(name, () => {
const k = keywords[name];
if (k.map) {
const expr = transpiler(name);
expect(expr).toEqual(k.map());
} else {
const transpile = () => transpiler(name);
expect(transpile).toThrow();
expect(transpile).not.toThrowError(RangeError);
}
});
}
}
export function testProperties(properties: PropertyDict, transpiler: Transpiler) {
for (const name in properties) {
const p = properties[name];
p['@examples'].forEach(example => {
it(name, () => {
if (!p.isUnsupported) {
transpiler(example);
} else {
const transpile = () => transpiler(example);
expect(transpile).toThrow();
expect(transpile).not.toThrowError(RangeError);
}
});
});
it(name, () => {
if (!p['@examples'].length) {
throw Error(`'${name}' property has no example(s)`);
}
});
}
}
export function testOperators(operators: OperatorList, transpiler: Transpiler) {
operators.forEach(o => {
o['@examples'].forEach(example => {
it(o.name, () => {
if (!o.isUnsupported) {
transpiler(example);
} else {
const transpile = () => transpiler(example);
expect(transpile).toThrow();
expect(transpile).not.toThrowError(RangeError);
}
});
});
it(o.name, () => {
if (!o['@examples'].length) {
throw Error(`'${o.name}' operator has no example(s)`);
}
});
});
}

View File

@@ -1,60 +0,0 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*/
import * as u from './utils';
import { transpiler } from '../vmd/parser';
import { keywords } from '../vmd/keywords';
import { properties } from '../vmd/properties';
import { operators } from '../vmd/operators';
const general = {
supported: [
// trimming
' name CA ',
'name CA ',
' name CA',
],
unsupported: [
// variables
'name $atomname',
'protein and @myselection',
// values outside of comparisons
'foobar',
'34',
'name',
'abs(-42)',
'abs(21+21)',
'sqr(3)',
'sqr(x)',
'sqr(x+33)',
'protein or foobar',
'34 and protein',
'name or protein',
]
};
describe('vmd general', () => {
general.supported.forEach(str => {
it(str, () => {
transpiler(str);
// compile(expr);
});
});
general.unsupported.forEach(str => {
it(str, () => {
const transpileStr = () => transpiler(str);
expect(transpileStr).toThrow();
expect(transpileStr).not.toThrowError(RangeError);
});
});
});
describe('vmd keywords', () => u.testKeywords(keywords, transpiler));
describe('vmd operators', () => u.testOperators(operators, transpiler));
describe('vmd properties', () => u.testProperties(properties, transpiler));

View File

@@ -1,17 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*
* Adapted from MolQL project
*/
import { transpiler as jmol } from './jmol/parser';
import { transpiler as pymol } from './pymol/parser';
import { transpiler as vmd } from './vmd/parser';
export const _transpiler = {
pymol,
vmd,
jmol,
};

View File

@@ -1,385 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*
* Adapted from MolQL project
*/
import * as P from '../../mol-util/monadic-parser';
import { MolScriptBuilder } from '../../mol-script/language/builder';
const B = MolScriptBuilder;
import { Expression } from '../language/expression';
import { KeywordDict, PropertyDict, FunctionDict, OperatorList } from './types';
export function escapeRegExp(s: String) {
return String(s).replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
}
// Takes a parser for the prefix operator, and a parser for the base thing being
// parsed, and parses as many occurrences as possible of the prefix operator.
// Note that the parser is created using `P.lazy` because it's recursive. It's
// valid for there to be zero occurrences of the prefix operator.
export function prefix(opParser: P.MonadicParser<any>, nextParser: P.MonadicParser<any>, mapFn: any) {
const parser: P.MonadicParser<any> = P.MonadicParser.lazy(() => {
return P.MonadicParser.seq(opParser, parser)
.map(x => mapFn(...x))
.or(nextParser);
});
return parser;
}
// Ideally this function would be just like `PREFIX` but reordered like
// `P.seq(parser, opParser).or(nextParser)`, but that doesn't work. The
// reason for that is that Parsimmon will get stuck in infinite recursion, since
// the very first rule. Inside `parser` is to match parser again. Alternatively,
// you might think to try `nextParser.or(P.seq(parser, opParser))`, but
// that won't work either because in a call to `.or` (aka `P.alt`), Parsimmon
// takes the first possible match, even if subsequent matches are longer, so the
// parser will never actually look far enough ahead to see the postfix
// operators.
export function postfix(opParser: P.MonadicParser<any>, nextParser: P.MonadicParser<any>, mapFn: any) {
// Because we can't use recursion like stated above, we just match a flat list
// of as many occurrences of the postfix operator as possible, then use
// `.reduce` to manually nest the list.
//
// Example:
//
// INPUT :: "4!!!"
// PARSE :: [4, "factorial", "factorial", "factorial"]
// REDUCE :: ["factorial", ["factorial", ["factorial", 4]]]
return P.MonadicParser.seqMap(
nextParser,
opParser.many(),
(x: any, suffixes: any) =>
suffixes.reduce((acc: any, x: any) => {
return mapFn(x, acc);
}, x)
);
}
// Takes a parser for all the operators at this precedence level, and a parser
// that parsers everything at the next precedence level, and returns a parser
// that parses as many binary operations as possible, associating them to the
// right. (e.g. 1^2^3 is 1^(2^3) not (1^2)^3)
export function binaryRight(opParser: P.MonadicParser<any>, nextParser: P.MonadicParser<any>, mapFn: any) {
const parser: P.MonadicParser<any> = P.MonadicParser.lazy(() =>
nextParser.chain(next =>
P.MonadicParser.seq(
opParser,
P.MonadicParser.of(next),
parser
).map((x) => {
return x;
}).or(P.MonadicParser.of(next))
)
);
return parser;
}
// Takes a parser for all the operators at this precedence level, and a parser
// that parsers everything at the next precedence level, and returns a parser
// that parses as many binary operations as possible, associating them to the
// left. (e.g. 1-2-3 is (1-2)-3 not 1-(2-3))
export function binaryLeft(opParser: P.MonadicParser<any>, nextParser: P.MonadicParser<any>, mapFn: any) {
// We run into a similar problem as with the `POSTFIX` parser above where we
// can't recurse in the direction we want, so we have to resort to parsing an
// entire list of operator chunks and then using `.reduce` to manually nest
// them again.
//
// Example:
//
// INPUT :: "1+2+3"
// PARSE :: [1, ["+", 2], ["+", 3]]
// REDUCE :: ["+", ["+", 1, 2], 3]
return P.MonadicParser.seqMap(
nextParser,
P.MonadicParser.seq(opParser, nextParser).many(),
(first: any, rest: any) => {
return rest.reduce((acc: any, ch: any) => {
const [op, another] = ch;
return mapFn(op, acc, another);
}, first);
}
);
}
/**
* combine operators of decreasing binding strength
*/
export function combineOperators(opList: any[], rule: P.MonadicParser<any>) {
const x = opList.reduce(
(acc, level) => {
const map = level.isUnsupported ? makeError(`operator '${level.name}' not supported`) : level.map;
return level.type(level.rule, acc, map);
},
rule
);
return x;
}
export function infixOp(re: RegExp, group: number = 0) {
return P.MonadicParser.optWhitespace.then(P.MonadicParser.regexp(re, group).skip(P.MonadicParser.optWhitespace));
}
export function prefixOp(re: RegExp, group: number = 0) {
return P.MonadicParser.regexp(re, group).skip(P.MonadicParser.optWhitespace);
}
export function postfixOp(re: RegExp, group: number = 0) {
return P.MonadicParser.optWhitespace.then(P.MonadicParser.regexp(re, group));
}
export function ofOp(name: string, short?: string) {
const op = short ? `${name}|${escapeRegExp(short)}` : name;
const re = RegExp(`(${op})\\s+([-+]?[0-9]*\\.?[0-9]+)\\s+OF`, 'i');
return infixOp(re, 2).map(parseFloat);
}
export function makeError(msg: string) {
return function () {
throw new Error(msg);
};
}
export function andExpr(selections: any[]) {
if (selections.length === 1) {
return selections[0];
} else if (selections.length > 1) {
return B.core.logic.and(selections);
} else {
return undefined;
}
}
export function orExpr(selections: any[]) {
if (selections.length === 1) {
return selections[0];
} else if (selections.length > 1) {
return B.core.logic.or(selections);
} else {
return undefined;
}
}
export function testExpr(property: any, args: any) {
if (args && args.op !== undefined && args.val !== undefined) {
const opArgs = [property, args.val];
switch (args.op) {
case '=': return B.core.rel.eq(opArgs);
case '!=': return B.core.rel.neq(opArgs);
case '>': return B.core.rel.gr(opArgs);
case '<': return B.core.rel.lt(opArgs);
case '>=': return B.core.rel.gre(opArgs);
case '<=': return B.core.rel.lte(opArgs);
default: throw new Error(`operator '${args.op}' not supported`);
}
} else if (args && args.flags !== undefined) {
return B.core.flags.hasAny([property, args.flags]);
} else if (args && args.min !== undefined && args.max !== undefined) {
return B.core.rel.inRange([property, args.min, args.max]);
} else if (!Array.isArray(args)) {
return B.core.rel.eq([property, args]);
} else if (args.length > 1) {
return B.core.set.has([B.core.type.set(args), property]);
} else {
return B.core.rel.eq([property, args[0]]);
}
}
export function invertExpr(selection: Expression) {
return B.struct.generator.queryInSelection({
0: selection, query: B.struct.generator.all(), 'in-complement': true }
);
}
export function strLenSortFn(a: string, b: string) {
return a.length < b.length ? 1 : -1;
}
function getNamesRegex(name: string, abbr?: string[]) {
const names = (abbr ? [name].concat(abbr) : [name])
.sort(strLenSortFn).map(escapeRegExp).join('|');
return RegExp(`${names}`, 'i');
}
export function getPropertyRules(properties: PropertyDict) {
// in keyof typeof properties
const propertiesDict: { [name: string]: P.MonadicParser<any> } = {};
Object.keys(properties).sort(strLenSortFn).forEach(name => {
const ps = properties[name];
const errorFn = makeError(`property '${name}' not supported`);
const rule = P.MonadicParser.regexp(ps.regex).map((x: any) => {
if (ps.isUnsupported) errorFn();
return testExpr(ps.property, ps.map(x));
});
if (!ps.isNumeric) {
propertiesDict[name] = rule;
}
});
return propertiesDict;
}
export function getNamedPropertyRules(properties: PropertyDict) {
const namedPropertiesList: P.MonadicParser<any>[] = [];
Object.keys(properties).sort(strLenSortFn).forEach(name => {
const ps = properties[name];
const errorFn = makeError(`property '${name}' not supported`);
const rule = P.MonadicParser.regexp(ps.regex).map((x: any) => {
if (ps.isUnsupported) errorFn();
return testExpr(ps.property, ps.map(x));
});
const nameRule = P.MonadicParser.regexp(getNamesRegex(name, ps.abbr)).trim(P.MonadicParser.optWhitespace);
const groupMap = (x: any) => B.struct.generator.atomGroups({ [ps.level]: x });
if (ps.isNumeric) {
namedPropertiesList.push(
nameRule.then(P.MonadicParser.seq(
P.MonadicParser.regexp(/>=|<=|=|!=|>|</).trim(P.MonadicParser.optWhitespace),
P.MonadicParser.regexp(ps.regex).map(ps.map)
)).map((x: any) => {
if (ps.isUnsupported) errorFn();
return testExpr(ps.property, { op: x[0], val: x[1] });
}).map(groupMap)
);
} else {
namedPropertiesList.push(nameRule.then(rule).map(groupMap));
}
});
return namedPropertiesList;
}
export function getKeywordRules(keywords: KeywordDict) {
const keywordsList: P.MonadicParser<any>[] = [];
Object.keys(keywords).sort(strLenSortFn).forEach(name => {
const ks = keywords[name];
const mapFn = ks.map ? ks.map : makeError(`keyword '${name}' not supported`);
const rule = P.MonadicParser.regexp(getNamesRegex(name, ks.abbr)).map(mapFn);
keywordsList.push(rule);
});
return keywordsList;
}
export function getFunctionRules(functions: FunctionDict, argRule: P.MonadicParser<any>) {
const functionsList: P.MonadicParser<any>[] = [];
const begRule = P.MonadicParser.regexp(/\(\s*/);
const endRule = P.MonadicParser.regexp(/\s*\)/);
Object.keys(functions).sort(strLenSortFn).forEach(name => {
const fs = functions[name];
const mapFn = fs.map ? fs.map : makeError(`function '${name}' not supported`);
const rule = P.MonadicParser.regexp(new RegExp(name, 'i')).skip(begRule).then(argRule).skip(endRule).map(mapFn);
functionsList.push(rule);
});
return functionsList;
}
export function getPropertyNameRules(properties: PropertyDict, lookahead: RegExp) {
const list: P.MonadicParser<any>[] = [];
Object.keys(properties).sort(strLenSortFn).forEach(name => {
const ps = properties[name];
const errorFn = makeError(`property '${name}' not supported`);
const rule = (P.MonadicParser as any).regexp(getNamesRegex(name, ps.abbr)).lookahead(lookahead).map(() => {
if (ps.isUnsupported) errorFn();
return ps.property;
});
list.push(rule);
});
return list;
}
export function getReservedWords(properties: PropertyDict, keywords: KeywordDict, operators: OperatorList, functions?: FunctionDict) {
const w: string[] = [];
for (const name in properties) {
w.push(name);
if (properties[name].abbr) w.push(...properties[name].abbr!);
}
for (const name in keywords) {
w.push(name);
if (keywords[name].abbr) w.push(...keywords[name].abbr!);
}
operators.forEach(o => {
w.push(o.name);
if (o.abbr) w.push(...o.abbr);
});
return w;
}
export function atomNameSet(ids: string[]) {
return B.core.type.set(ids.map(B.atomName));
}
export function asAtoms(e: Expression) {
return B.struct.generator.queryInSelection({
0: e,
query: B.struct.generator.all()
});
}
export function wrapValue(property: any, value: any, sstrucDict?: any) {
switch (property.head.name) {
case 'structure-query.atom-property.macromolecular.label_atom_id':
return B.atomName(value);
case 'structure-query.atom-property.core.element-symbol':
return B.es(value);
case 'structure-query.atom-property.macromolecular.secondary-structure-flags':
if (sstrucDict) {
value = [sstrucDict[value.toUpperCase()] || 'none'];
}
return B.struct.type.secondaryStructureFlags([value]);
default:
return value;
}
}
const propPrefix = 'structure-query.atom-property.macromolecular.';
const entityProps = ['entityKey', 'label_entity_id', 'entityType'];
const chainProps = ['chainKey', 'label_asym_id', 'label_entity_id', 'auth_asym_id', 'entityType'];
const residueProps = ['residueKey', 'label_comp_id', 'label_seq_id', 'auth_comp_id', 'auth_seq_id', 'pdbx_formal_charge', 'secondaryStructureKey', 'secondaryStructureFlags', 'isModified', 'modifiedParentName'];
export function testLevel(property: any) {
if (property.head.name.startsWith(propPrefix)) {
const name = property.head.name.substr(propPrefix.length);
if (entityProps.includes(name)) return 'entity-test';
if (chainProps.includes(name)) return 'chain-test';
if (residueProps.includes(name)) return 'residue-test';
}
return 'atom-test';
}
const flagProps = [
'structure-query.atom-property.macromolecular.secondary-structure-flags'
];
export function valuesTest(property: any, values: any[]) {
if (flagProps.includes(property.head.name)) {
const name = values[0].head;
const flags: any[] = [];
values.forEach(v => flags.push(...v.args[0]));
return B.core.flags.hasAny([property, { head: name, args: flags }]);
} else {
if (values.length === 1) {
return B.core.rel.eq([property, values[0]]);
} else if (values.length > 1) {
return B.core.set.has([B.core.type.set(values), property]);
}
}
}
export function resnameExpr(resnameList: string[]) {
return B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(resnameList),
B.ammp('label_comp_id')
])
});
}

View File

@@ -1,28 +0,0 @@
/**
* Copyright (c) 2017-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>
*
* Adapted from MolQL project
*/
export const examples = [{
name: 'Residue 50 or 135',
value: '50 or 135'
}, {
name: 'Atoms with no covalent bonds',
value: 'bondcount = 0'
}, {
name: 'All 3-10 helices',
value: 'substructure = "helix310"'
}, {
name: 'Metal atoms',
value: 'metal'
}, {
name: 'Atoms invloved in aromatic bonds',
value: 'isAromatic'
}, {
name: 'Pyrimidine residues',
value: 'pyrimidine'
}];

View File

@@ -1,571 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
* Adapted from MolQL project
*/
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import * as h from '../helper';
import { KeywordDict } from '../types';
const ResDict = {
acidic: ['ASP', 'GLU'],
aliphatic: ['ALA', 'GLY', 'ILE', 'LEU', 'VAL'],
amino: ['ALA', 'ARG', 'ASN', 'ASP', 'CYS', 'GLN', 'GLU', 'GLY', 'HIS', 'ILE', 'LEU', 'LYS', 'MET', 'PHE', 'PRO', 'SER', 'THR', 'TRP', 'TYR', 'VAL', 'ASX', 'GLX', 'UNK'],
aromatic: ['HIS', 'PHE', 'TRP', 'TYR'],
basic: ['ARG', 'HIS', 'LYS'],
buried: ['ALA', 'CYS', 'ILE', 'LEU', 'MET', 'PHE', 'TRP', 'VAL'],
cg: ['CYT', 'C', 'GUA', 'G'],
cyclic: ['HIS', 'PHE', 'PRO', 'TRP', 'TYR'],
hydrophobic: ['ALA', 'GLY', 'ILE', 'LEU', 'MET', 'PHE', 'PRO', 'TRP', 'TYR', 'VAL'],
large: ['ARG', 'GLU', 'GLN', 'HIS', 'ILE', 'LEU', 'LYS', 'MET', 'PHE', 'TRP', 'TYR'],
medium: ['ASN', 'ASP', 'CYS', 'PRO', 'THR', 'VAL'],
small: ['ALA', 'GLY', 'SER'],
nucleic: ['G', 'C', 'A', 'T', 'U', 'I', 'DG', 'DC', 'DA', 'DT', 'DU', 'DI', '+G', '+C', '+A', '+T', '+U', '+I']
};
const Backbone = {
nucleic: ['P', "O3'", "O5'", "C5'", "C4'", "C3'", 'OP1', 'OP2', 'O3*', 'O5*', 'C5*', 'C4*', 'C3*',
"C2'", "C1'", "O4'", "O2'"],
protein: ['C', 'N', 'CA']
};
function nucleicExpr() {
return B.struct.combinator.merge([
B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.set(...ResDict.nucleic),
B.ammp('label_comp_id')
])
}),
B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.logic.and([
B.core.rel.eq([B.struct.atomSet.atomCount(), 1]),
B.core.rel.eq([B.ammp('label_atom_id'), B.atomName('P')]),
])
}),
B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.logic.or([
B.core.set.isSubset([
h.atomNameSet(["C1'", "C2'", "O3'", "C3'", "C4'", "C5'", "O5'"]),
B.ammpSet('label_atom_id')
]),
B.core.set.isSubset([
h.atomNameSet(['C1*', 'C2*', 'O3*', 'C3*', 'C4*', 'C5*', 'O5*']),
B.ammpSet('label_atom_id')
])
])
})
]);
}
// TODO: improve, see keywords.protein['@desc'] below
function proteinExpr() {
return B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.set(...ResDict.amino),
B.ammp('label_comp_id')
])
});
}
// TODO: improve, see keywords.backbone['@desc'] below
function backboneExpr() {
return B.struct.combinator.merge([
B.struct.modifier.intersectBy({
0: B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.amino),
B.ammp('label_comp_id')
])
}),
by: B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.core.type.set(Backbone.protein),
B.ammp('label_atom_id')
])
})
}),
B.struct.modifier.intersectBy({
0: B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.nucleic),
B.ammp('label_comp_id')
])
}),
by: B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.core.type.set(Backbone.nucleic),
B.ammp('label_atom_id')
])
})
}),
]);
}
export const keywords: KeywordDict = {
// general terms
all: {
'@desc': 'all atoms; same as *',
abbr: ['*'],
map: () => B.struct.generator.all()
},
bonded: {
'@desc': 'covalently bonded',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.gr([B.struct.atomProperty.core.bondCount({
flags: B.struct.type.bondFlags(['covalent', 'metallic', 'sulfide'])
}), 0])
})
},
clickable: {
'@desc': 'actually visible -- having some visible aspect such as wireframe, spacefill, or a label showing, or the alpha-carbon or phosphorus atom in a biomolecule that is rendered with only cartoon, rocket, or other biomolecule-specific shape.'
},
connected: {
'@desc': 'bonded in any way, including hydrogen bonds',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.gr([B.struct.atomProperty.core.bondCount({
flags: B.struct.type.bondFlags()
}), 0])
})
},
displayed: {
'@desc': 'displayed using the display or hide command; not necessarily visible'
},
hidden: {
'@desc': 'hidden using the display or hide command'
},
none: {
'@desc': 'no atoms',
map: () => B.struct.generator.empty()
},
selected: {
'@desc': 'atoms that have been selected; defaults to all when a file is first loaded'
},
thisModel: {
'@desc': 'atoms in the current frame set, as defined by frame, model, or animation commands. If more than one model is in this set, "thisModel" refers to all of them, regardless of atom displayed/hidden status.'
},
visible: {
'@desc': 'visible in any way, including PDB residue atoms for which a cartoon or other such rendering makes their group visible, even if they themselves are not visible.'
},
subset: {
'@desc': 'the currently defined subset. Note that if a subset is currently defined, then select/display all is the same as select/display subset, restrict none is the same as restrict not subset. In addition, select not subset selects nothing.'
},
specialPosition: {
'@desc': 'atoms in crystal structures that are at special positions - that is, for which there is more than one operator that leads to them.'
},
unitcell: {
'@desc': 'atoms within the current unitcell, which may be offset. This includes atoms on the faces and at the vertices of the unitcell.'
},
polyhedra: {
'@desc': 'all central atoms for which polyhedra have been created. See also polyhera(n), below. (Jmol 14.4)'
},
nonmetal: {
'@desc': '_H,_He,_B,_C,_N,_O,_F,_Ne,_Si,_P,_S,_Cl,_Ar,_As,_Se,_Br,_Kr,_Te,_I,_Xe,_At,_Rn',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.set(...['H', 'He', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Si', 'P', 'S', 'Cl', 'Ar', 'As', 'Se', 'Br', 'Kr', 'Te', 'I', 'Xe', 'At', 'Rn'].map(B.es)),
B.acp('elementSymbol')
])
})
},
metal: {
'@desc': '!nonmetal',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.logic.not([
B.core.set.has([
B.set(...['H', 'He', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Si', 'P', 'S', 'Cl', 'Ar', 'As', 'Se', 'Br', 'Kr', 'Te', 'I', 'Xe', 'At', 'Rn'].map(B.es)),
B.acp('elementSymbol')
])
])
})
},
alkaliMetal: {
'@desc': '_Li,_Na,_K,_Rb,_Cs,_Fr',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.set(...['Li', 'Na', 'K', 'Rb', 'Cs', 'Fr'].map(B.es)),
B.acp('elementSymbol')
])
})
},
alkalineEarth: {
'@desc': '_Be,_Mg,_Ca,_Sr,_Ba,_Ra',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.set(...['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra'].map(B.es)),
B.acp('elementSymbol')
])
})
},
nobleGas: {
'@desc': '_He,_Ne,_Ar,_Kr,_Xe,_Rn',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.set(...['He', 'Ne', 'Ar', 'Kr', 'Xe', 'Rn'].map(B.es)),
B.acp('elementSymbol')
])
})
},
metalloid: {
'@desc': '_B,_Si,_Ge,_As,_Sb,_Te',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.set(...['B', 'Si', 'Ge', 'As', 'Sb', 'Te'].map(B.es)),
B.acp('elementSymbol')
])
})
},
transitionMetal: {
'@desc': '(includes La and Ac) elemno>=21 and elemno<=30, elemno=57, elemno=89, elemno>=39 and elemno<=48, elemno>=72 and elemno<=80, elemno>=104 and elemno<=112',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.logic.or([
B.core.rel.inRange([B.acp('atomicNumber'), 21, 30]),
B.core.rel.inRange([B.acp('atomicNumber'), 39, 48]),
B.core.rel.inRange([B.acp('atomicNumber'), 72, 80]),
B.core.rel.inRange([B.acp('atomicNumber'), 104, 112]),
B.core.set.has([B.set(57, 89), B.acp('atomicNumber')])
])
})
},
lanthanide: {
'@desc': '(does not include La) elemno>57 and elemno<=71',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.inRange([B.acp('atomicNumber'), 57, 71])
})
},
actinide: {
'@desc': '(does not include Ac) elemno>89 and elemno<=103',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.inRange([B.acp('atomicNumber'), 89, 103])
})
},
isaromatic: {
'@desc': 'atoms connected with the AROMATIC, AROMATICSINGLE, or AROMATICDOUBLE bond types',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.gr([
B.struct.atomProperty.core.bondCount({
flags: B.struct.type.bondFlags(['aromatic'])
}),
0
])
})
},
carbohydrate: {
'@desc': ''
},
ions: {
'@desc': '(specifically the PDB designations "PO4" and "SO4")'
},
ligand: {
'@desc': '(originally "hetero and not solvent"; changed to "!(protein,nucleic,water,UREA)" for Jmol 12.2)'
},
nucleic: {
'@desc': 'any group that (a) has one of the following group names: G, C, A, T, U, I, DG, DC, DA, DT, DU, DI, +G, +C, +A, +T, +U, +I; or (b) can be identified as a group that is only one atom, with name "P"; or (c) has all of the following atoms (prime, \', can replace * here): C1*, C2*, C3*, O3*, C4*, C5*, and O5*.',
map: () => nucleicExpr()
},
purine: {
'@desc': 'any nucleic group that (a) has one of the following group names: A, G, I, DA, DG, DI, +A, +G, or +I; or (b) also has atoms N7, C8, and N9.',
map: () => B.struct.modifier.intersectBy({
0: nucleicExpr(),
by: B.struct.combinator.merge([
B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.set(...['A', 'G', 'I', 'DA', 'DG', 'DI', '+A', '+G', '+I']),
B.ammp('label_comp_id')
])
}),
B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.set.isSubset([
h.atomNameSet(['N7', 'C8', 'N9']),
B.ammpSet('label_atom_id')
])
})
])
})
},
pyrimidine: {
'@desc': 'any nucleic group that (a) has one of the following group names: C, T, U, DC, DT, DU, +C, +T, +U; or (b) also has atom O2.',
map: () => B.struct.modifier.intersectBy({
0: nucleicExpr(),
by: B.struct.combinator.merge([
B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.set(...['C', 'T', 'U', 'DC', 'DT', 'DU', '+C', '+T', '+U']),
B.ammp('label_comp_id')
])
}),
B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.logic.or([
B.core.set.has([
B.ammpSet('label_atom_id'),
B.atomName('O2*')
]),
B.core.set.has([
B.ammpSet('label_atom_id'),
B.atomName("O2'")
])
])
})
])
})
},
dna: {
'@desc': 'any nucleic group that (a) has one of the following group names: DG, DC, DA, DT, DU, DI, T, +G, +C, +A, +T; or (b) has neither atom O2* or O2\'.',
map: () => B.struct.modifier.intersectBy({
0: nucleicExpr(),
by: B.struct.combinator.merge([
B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.set(...['DG', 'DC', 'DA', 'DT', 'DU', 'DI', 'T', '+G', '+C', '+A', '+T']),
B.ammp('label_comp_id')
])
}),
B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.logic.not([
B.core.logic.or([
B.core.set.has([
B.ammpSet('label_atom_id'),
B.atomName('O2*')
]),
B.core.set.has([
B.ammpSet('label_atom_id'),
B.atomName("O2'")
])
])
])
})
])
})
},
rna: {
'@desc': 'any nucleic group that (a) has one of the following group names: G, C, A, U, I, +U, +I; or (b) has atom O2* or O2\'.',
map: () => B.struct.modifier.intersectBy({
0: nucleicExpr(),
by: B.struct.combinator.merge([
B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.set(...['G', 'C', 'A', 'U', 'I', '+U', '+I']),
B.ammp('label_comp_id')
])
}),
B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.logic.or([
B.core.set.has([
B.ammpSet('label_atom_id'),
B.atomName('O2*')
]),
B.core.set.has([
B.ammpSet('label_atom_id'),
B.atomName("O2'")
])
])
})
])
})
},
protein: {
'@desc': 'defined as a group that (a) has one of the following group names: ALA, ARG, ASN, ASP, CYS, GLN, GLU, GLY, HIS, ILE, LEU, LYS, MET, PHE, PRO, SER, THR, TRP, TYR, VAL, ASX, GLX, or UNK; or (b) contains PDB atom designations [C, O, CA, and N] bonded correctly; or (c) does not contain "O" but contains [C, CA, and N] bonded correctly; or (d) has only one atom, which has name CA and does not have the group name CA (indicating a calcium atom).',
map: () => proteinExpr()
},
acidic: {
'@desc': 'ASP GLU',
map: () => h.resnameExpr(ResDict.acidic)
},
acyclic: {
'@desc': 'amino and not cyclic',
map: () => B.struct.modifier.intersectBy({
0: h.resnameExpr(ResDict.amino),
by: h.invertExpr(h.resnameExpr(ResDict.cyclic))
})
},
aliphatic: {
'@desc': 'ALA GLY ILE LEU VAL',
map: () => h.resnameExpr(ResDict.aliphatic)
},
amino: {
'@desc': 'all twenty standard amino acids, plus ASX, GLX, UNK',
map: () => h.resnameExpr(ResDict.amino)
},
aromatic: {
'@desc': 'HIS PHE TRP TYR (see also "isaromatic" for aromatic bonds)',
map: () => h.resnameExpr(ResDict.aromatic)
},
basic: {
'@desc': 'ARG HIS LYS',
map: () => h.resnameExpr(ResDict.basic)
},
buried: {
'@desc': 'ALA CYS ILE LEU MET PHE TRP VAL',
map: () => h.resnameExpr(ResDict.buried)
},
charged: {
'@desc': 'same as acidic or basic -- ASP GLU, ARG HIS LYS',
map: () => h.resnameExpr(ResDict.acidic.concat(ResDict.basic))
},
cyclic: {
'@desc': 'HIS PHE PRO TRP TYR',
map: () => h.resnameExpr(ResDict.cyclic)
},
helix: {
'@desc': 'secondary structure-related.',
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.flags.hasAny([
B.struct.type.secondaryStructureFlags(['helix']),
B.ammp('secondaryStructureFlags')
])
})
},
helixalpha: {
'@desc': 'secondary structure-related.',
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.flags.hasAny([
B.struct.type.secondaryStructureFlags(['alpha']),
B.ammp('secondaryStructureFlags')
])
})
},
helix310: {
'@desc': 'secondary structure-related.',
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.flags.hasAny([
B.struct.type.secondaryStructureFlags(['3-10']),
B.ammp('secondaryStructureFlags')
])
})
},
helixpi: {
'@desc': 'secondary structure-related.',
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.flags.hasAny([
B.struct.type.secondaryStructureFlags(['pi']),
B.ammp('secondaryStructureFlags')
])
})
},
hetero: {
'@desc': 'PDB atoms designated as HETATM',
map: () => B.struct.generator.atomGroups({
'atom-test': B.ammp('isHet')
})
},
hydrophobic: {
'@desc': 'ALA GLY ILE LEU MET PHE PRO TRP TYR VAL',
map: () => h.resnameExpr(ResDict.hydrophobic)
},
large: {
'@desc': 'ARG GLU GLN HIS ILE LEU LYS MET PHE TRP TYR',
map: () => h.resnameExpr(ResDict.large)
},
medium: {
'@desc': 'ASN ASP CYS PRO THR VAL',
map: () => h.resnameExpr(ResDict.medium)
},
negative: {
'@desc': 'same as acidic -- ASP GLU',
map: () => h.resnameExpr(ResDict.acidic)
},
neutral: {
'@desc': 'amino and not (acidic or basic)',
map: () => B.struct.modifier.intersectBy({
0: h.resnameExpr(ResDict.amino),
by: h.invertExpr(h.resnameExpr(ResDict.acidic.concat(ResDict.basic)))
})
},
polar: {
'@desc': 'amino and not hydrophobic',
map: () => B.struct.modifier.intersectBy({
0: h.resnameExpr(ResDict.amino),
by: h.invertExpr(h.resnameExpr(ResDict.hydrophobic))
})
},
positive: {
'@desc': 'same as basic -- ARG HIS LYS',
map: () => h.resnameExpr(ResDict.basic)
},
sheet: {
'@desc': 'secondary structure-related',
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.flags.hasAny([
B.struct.type.secondaryStructureFlags(['sheet']),
B.ammp('secondaryStructureFlags')
])
})
},
small: {
'@desc': 'ALA GLY SER',
map: () => h.resnameExpr(ResDict.small)
},
surface: {
'@desc': 'amino and not buried',
map: () => B.struct.modifier.intersectBy({
0: h.resnameExpr(ResDict.amino),
by: h.invertExpr(h.resnameExpr(ResDict.buried))
})
},
turn: {
'@desc': 'secondary structure-related',
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.flags.hasAny([
B.struct.type.secondaryStructureFlags(['turn']),
B.ammp('secondaryStructureFlags')
])
})
},
alpha: {
'@desc': '(*.CA)',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.eq([
B.atomName('CA'),
B.ammp('label_atom_id')
])
})
},
base: {
'@desc': '(nucleic bases)'
},
backbone: {
'@desc': '(*.C, *.CA, *.N, and all nucleic other than the bases themselves)',
abbr: ['mainchain'],
map: () => backboneExpr()
},
sidechain: {
'@desc': '((protein or nucleic) and not backbone)'
},
spine: {
'@desc': '(*.CA, *.N, *.C for proteins; *.P, *.O3\', *.O5\', *.C3\', *.C4\', *.C5 for nucleic acids)'
},
leadatom: {
'@desc': '(*.CA, *.P, and terminal *.O5\')'
},
solvent: {
'@desc': 'PDB "HOH", water, also the connected set of H-O-H in any model'
},
};

View File

@@ -1,64 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*
* Adapted from MolQL project
*/
import { properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
const _docs: string[] = [
'Jmol',
'============',
'--------------------------------',
''
];
_docs.push(`## Properties\n\n`);
_docs.push('--------------------------------\n');
for (const name in properties) {
if (properties[name].isUnsupported) continue;
const names = [name];
if (properties[name].abbr) names.push(...properties[name].abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (properties[name]['@desc']) {
_docs.push(`*${properties[name]['@desc']}*\n`);
}
}
_docs.push(`## Operators\n\n`);
_docs.push('--------------------------------\n');
operators.forEach(o => {
if (o.isUnsupported) return;
const names = [o.name];
if (o.abbr) names.push(...o.abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (o['@desc']) {
_docs.push(`*${o['@desc']}*\n`);
}
});
_docs.push(`## Keywords\n\n`);
_docs.push('--------------------------------\n');
for (const name in keywords) {
if (!keywords[name].map) continue;
const names = [name];
if (keywords[name].abbr) names.push(...keywords[name].abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (keywords[name]['@desc']) {
_docs.push(`*${keywords[name]['@desc']}*\n`);
}
}
export const docs = _docs.join('\n');

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
* Adapted from MolQL project
*/
import * as P from '../../../mol-util/monadic-parser';
import * as h from '../helper';
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { OperatorList } from '../types';
export const operators: OperatorList = [
{
'@desc': 'Selects atoms that are not included in s1.',
'@examples': ['not ARG'],
name: 'not',
type: h.prefix,
rule: P.MonadicParser.alt(P.MonadicParser.regex(/NOT/i).skip(P.MonadicParser.whitespace), P.MonadicParser.string('!').skip(P.MonadicParser.optWhitespace)),
map: (op, selection) => h.invertExpr(selection),
},
{
'@desc': 'Selects atoms included in both s1 and s2.',
'@examples': ['ASP and .CA'],
name: 'and',
type: h.binaryLeft,
rule: h.infixOp(/AND|&/i),
map: (op, selection, by) => B.struct.modifier.intersectBy({ 0: selection, by })
},
{
'@desc': 'Selects atoms included in either s1 or s2.',
'@examples': ['ASP or GLU'],
name: 'or',
type: h.binaryLeft,
rule: h.infixOp(/OR|\||,/i),
map: (op, s1, s2) => B.struct.combinator.merge([s1, s2])
}
];

View File

@@ -1,267 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Koya Sakuma < koya.sakuma.work@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
* Adapted from MolQL project
*/
import * as P from '../../../mol-util/monadic-parser';
import * as h from '../helper';
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { properties, structureMap } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
import { AtomGroupArgs } from '../types';
import { Transpiler } from '../transpiler';
import { OperatorList } from '../types';
import { Expression } from '../../language/expression';
// <, <=, =, >=, >, !=, and LIKE
const valueOperators: OperatorList = [
{
'@desc': 'value comparisons',
'@examples': [],
name: '=',
abbr: ['=='],
type: h.binaryLeft,
rule: P.MonadicParser.regexp(/\s*(LIKE|>=|<=|=|!=|>|<)\s*/i, 1),
map: (op, e1, e2) => {
let expr;
if (e1 === 'structure') {
expr = B.core.flags.hasAny([B.ammp('secondaryStructureFlags'), structureMap(e2)]);
} else if (e2 === 'structure') {
expr = B.core.flags.hasAny([B.ammp('secondaryStructureFlags'), structureMap(e1)]);
} else if (e1.head !== undefined) {
if (e1.head.name === 'core.type.regex') {
expr = B.core.str.match([e1, B.core.type.str([e2])]);
}
} else if (e2.head !== undefined) {
if (e2.head.name === 'core.type.regex') {
expr = B.core.str.match([e2, B.core.type.str([e1])]);
}
} else if (op.toUpperCase() === 'LIKE') {
if (e1.head) {
expr = B.core.str.match([
B.core.type.regex([`^${e2}$`, 'i']),
B.core.type.str([e1])
]);
} else {
expr = B.core.str.match([
B.core.type.regex([`^${e1}$`, 'i']),
B.core.type.str([e2])
]);
}
}
if (!expr) {
if (e1.head) e2 = h.wrapValue(e1, e2);
if (e2.head) e1 = h.wrapValue(e2, e1);
switch (op) {
case '=':
expr = B.core.rel.eq([e1, e2]);
break;
case '!=':
expr = B.core.rel.neq([e1, e2]);
break;
case '>':
expr = B.core.rel.gr([e1, e2]);
break;
case '<':
expr = B.core.rel.lt([e1, e2]);
break;
case '>=':
expr = B.core.rel.gre([e1, e2]);
break;
case '<=':
expr = B.core.rel.lte([e1, e2]);
break;
default: throw new Error(`value operator '${op}' not supported`);
}
}
return B.struct.generator.atomGroups({ 'atom-test': expr });
}
}
];
function atomExpressionQuery(x: any[]) {
const [resname, resnoRange, resno, inscode, chainname, atomname, altloc] = x[1];
const tests: AtomGroupArgs = {};
if (chainname) {
// TODO: should be configurable, there is an option in Jmol to use auth or label
tests['chain-test'] = B.core.rel.eq([B.ammp('auth_asym_id'), chainname]);
}
const resProps = [];
if (resname) resProps.push(B.core.rel.eq([B.ammp('label_comp_id'), resname]));
if (resnoRange) resProps.push(B.core.logic.and([
B.core.rel.gre([B.ammp('auth_seq_id'), resnoRange[0]]),
B.core.rel.lte([B.ammp('auth_seq_id'), resnoRange[1]])
]));
if (resno) resProps.push(B.core.rel.eq([B.ammp('auth_seq_id'), resno]));
if (inscode) resProps.push(B.core.rel.eq([B.ammp('pdbx_PDB_ins_code'), inscode]));
if (resProps.length) tests['residue-test'] = h.andExpr(resProps);
const atomProps = [];
if (atomname) atomProps.push(B.core.rel.eq([B.ammp('auth_atom_id'), atomname]));
if (altloc) atomProps.push(B.core.rel.eq([B.ammp('label_alt_id'), altloc]));
if (atomProps.length) tests['atom-test'] = h.andExpr(atomProps);
return B.struct.generator.atomGroups(tests);
}
const lang = P.MonadicParser.createLanguage({
Integer: () => P.MonadicParser.regexp(/-?[0-9]+/).map(Number).desc('integer'),
Parens: function (r: any) {
return P.MonadicParser.alt(
r.Parens,
r.Operator,
r.Expression
).wrap(P.MonadicParser.regexp(/\(\s*/), P.MonadicParser.regexp(/\s*\)/));
},
Expression: function (r: any) {
return P.MonadicParser.alt(
r.Keywords,
r.AtomExpression.map(atomExpressionQuery),
r.Within.map((x: [number, Expression]) => B.struct.modifier.includeSurroundings({ 0: x[1], radius: x[0] })),
r.ValueQuery,
r.Element.map((x: string) => B.struct.generator.atomGroups({
'atom-test': B.core.rel.eq([B.acp('elementSymbol'), B.struct.type.elementSymbol(x)])
})),
r.Resname.map((x: string) => B.struct.generator.atomGroups({
'residue-test': B.core.rel.eq([B.ammp('label_comp_id'), x])
})),
);
},
Operator: function (r: any) {
return h.combineOperators(operators, P.MonadicParser.alt(r.Parens, r.Expression));
},
AtomExpression: function (r: any) {
return P.MonadicParser.seq(
P.MonadicParser.lookahead(r.AtomPrefix),
P.MonadicParser.seq(
r.BracketedResname.or(P.MonadicParser.of(null)),
r.ResnoRange.or(P.MonadicParser.of(null)),
r.Resno.or(P.MonadicParser.of(null)),
r.Inscode.or(P.MonadicParser.of(null)),
r.Chainname.or(P.MonadicParser.of(null)),
r.Atomname.or(P.MonadicParser.of(null)),
r.Altloc.or(P.MonadicParser.of(null)),
r.Model.or(P.MonadicParser.of(null))
)
).desc('expression');
},
AtomPrefix: () => P.MonadicParser.regexp(/[\[0-9:^%/.-]/).desc('atom-prefix'),
Chainname: () => P.MonadicParser.regexp(/:([A-Za-z]{1,3})/, 1).desc('chainname'),
Model: () => P.MonadicParser.regexp(/\/([0-9]+)/, 1).map(Number).desc('model'),
Element: () => P.MonadicParser.regexp(/_([A-Za-z]{1,3})/, 1).desc('element'),
Atomname: () => P.MonadicParser.regexp(/\.([a-zA-Z0-9]{1,4})/, 1).map(B.atomName).desc('atomname'),
Resname: () => P.MonadicParser.regexp(/[a-zA-Z0-9]{1,4}/).desc('resname'),
Resno: (r: any) => r.Integer.desc('resno'),
Altloc: () => P.MonadicParser.regexp(/%([a-zA-Z0-9])/, 1).desc('altloc'),
Inscode: () => P.MonadicParser.regexp(/\^([a-zA-Z0-9])/, 1).desc('inscode'),
BracketedResname: () => P.MonadicParser.regexp(/\[([a-zA-Z0-9]{1,4})\]/, 1).desc('bracketed-resname'),
ResnoRange: (r: any) => {
return P.MonadicParser.seq(
r.Integer.skip(P.MonadicParser.seq(
P.MonadicParser.optWhitespace,
P.MonadicParser.string('-'),
P.MonadicParser.optWhitespace
)),
r.Integer
).desc('resno-range');
},
Within: (r: any) => {
return P.MonadicParser.regexp(/within/i)
.skip(P.MonadicParser.regexp(/\s*\(\s*/))
.then(P.MonadicParser.seq(
r.Integer.skip(P.MonadicParser.regexp(/\s*,\s*/)),
r.Query
))
.skip(P.MonadicParser.regexp(/\)/));
},
Keywords: () => P.MonadicParser.alt(...h.getKeywordRules(keywords)).desc('keyword'),
Query: function (r: any) {
return P.MonadicParser.alt(
r.Operator,
r.Parens,
r.Expression
).trim(P.MonadicParser.optWhitespace);
},
Number: function () {
return P.MonadicParser.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/)
.map(Number)
.desc('number');
},
String: function () {
const w = h.getReservedWords(properties, keywords, operators)
.sort(h.strLenSortFn).map(h.escapeRegExp).join('|');
return P.MonadicParser.alt(
P.MonadicParser.regexp(new RegExp(`(?!(${w}))[A-Z0-9_]+`, 'i')),
P.MonadicParser.regexp(/'((?:[^"\\]|\\.)*)'/, 1),
P.MonadicParser.regexp(/"((?:[^"\\]|\\.)*)"/, 1).map(x => B.core.type.regex([`^${x}$`, 'i']))
).desc('string');
},
Value: function (r: any) {
return P.MonadicParser.alt(r.Number, r.String);
},
ValueParens: function (r: any) {
return P.MonadicParser.alt(
r.ValueParens,
r.ValueOperator,
r.ValueExpressions
).wrap(P.MonadicParser.string('('), P.MonadicParser.string(')'));
},
ValuePropertyNames: function () {
return P.MonadicParser.alt(...h.getPropertyNameRules(properties, /LIKE|>=|<=|=|!=|>|<|\)|\s/i));
},
ValueOperator: function (r: any) {
return h.combineOperators(valueOperators, P.MonadicParser.alt(r.ValueParens, r.ValueExpressions));
},
ValueExpressions: function (r: any) {
return P.MonadicParser.alt(
r.Value,
r.ValuePropertyNames
);
},
ValueQuery: function (r: any) {
return P.MonadicParser.alt(
r.ValueOperator.map((x: any) => {
if (x.head) {
if (x.head.name.startsWith('structure-query.generator')) return x;
} else {
if (typeof x === 'string' && x.length <= 4) {
return B.struct.generator.atomGroups({
'residue-test': B.core.rel.eq([B.ammp('label_comp_id'), x])
});
}
}
throw new Error(`values must be part of an comparison, value '${x}'`);
})
);
}
});
export const transpiler: Transpiler = str => lang.Query.tryParse(str);

View File

@@ -1,667 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*
* Adapted from MolQL project
*/
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { PropertyDict } from '../types';
const reFloat = /[-+]?[0-9]*\.?[0-9]+/;
const rePosInt = /[0-9]+/;
function str(x: string) { return x; }
const structureDict: { [key: string]: string } = {
none: 'none',
turn: 'turn',
sheet: 'beta',
helix: 'helix',
dna: 'dna',
rna: 'rna',
carbohydrate: 'carbohydrate',
helix310: '3-10',
helixalpha: 'alpha',
helixpi: 'pi',
0: 'none',
1: 'turn',
2: 'beta',
3: 'helix',
4: 'dna',
5: 'rna',
6: 'carbohydrate',
7: '3-10',
8: 'alpha',
9: 'pi',
};
export function structureMap(x: any) {
if (x.head) {
if (x.head.name && x.head.name === 'core.type.regex') x = x.args[0].replace(/^\^|\$$/g, '');
x = structureDict[x.toString().toLowerCase()] || 'none';
if (['dna', 'rna', 'carbohydrate'].indexOf(x) !== -1) {
throw new Error("values 'dna', 'rna', 'carbohydrate' not yet supported for 'structure' property");
} else {
return B.struct.type.secondaryStructureFlags([x]);
}
}
}
export const properties: PropertyDict = {
adpmax: {
'@desc': 'the maximum anisotropic displacement parameter for the selected atom',
'@examples': [''],
isUnsupported: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test'
},
adpmin: {
'@desc': 'the minimum anisotropic displacement parameter for the selected atom',
'@examples': [''],
isUnsupported: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test'
},
altloc: {
'@desc': 'PDB alternate location identifier',
'@examples': ['altloc = A'],
regex: /[a-zA-Z0-9]/, map: str,
level: 'atom-test', property: B.ammp('label_alt_id')
},
altname: {
'@desc': 'an alternative name given to atoms by some file readers (for example, P2N)',
'@examples': [''],
isUnsupported: true,
regex: /[a-zA-Z0-9]/, map: str,
level: 'atom-test'
},
atomID: {
'@desc': 'special atom IDs for PDB atoms assigned by Jmol',
'@examples': [''],
isUnsupported: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test'
},
atomIndex: {
'@desc': 'atom 0-based index; a unique number for each atom regardless of the number of models loaded',
'@examples': [''],
isUnsupported: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test'
},
atomName: {
'@desc': 'atom name',
'@examples': ['atomName = CA'],
regex: /[a-zA-Z0-9]+/, map: v => B.atomName(v),
level: 'atom-test', property: B.ammp('label_atom_id')
},
atomno: {
'@desc': 'sequential number; you can use "@" instead of "atomno=" -- for example, select @33 or Var x = @33 or @35',
'@examples': [''],
isUnsupported: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test'
},
atomType: {
'@desc': 'atom type (mol2, AMBER files) or atom name (other file types)',
'@examples': ['atomType = OH'],
regex: /[a-zA-Z0-9]+/, map: v => B.atomName(v),
level: 'atom-test', property: B.ammp('label_atom_id')
},
atomX: {
'@desc': 'Cartesian X coordinate (or just X)',
'@examples': ['x = 4.2'],
abbr: ['X'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('x')
},
atomY: {
'@desc': 'Cartesian Y coordinate (or just Y)',
'@examples': ['y < 42'],
abbr: ['Y'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('y')
},
atomZ: {
'@desc': 'Cartesian Z coordinate (or just Z)',
'@examples': ['Z > 10'],
abbr: ['Z'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('z')
},
bondcount: {
'@desc': 'covalent bond count',
'@examples': ['bondcount = 0'],
isNumeric: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test', property: B.acp('bondCount')
},
bondingRadius: {
'@desc': 'radius used for auto bonding; synonymous with ionic and ionicRadius',
'@examples': [''],
abbr: ['ionic', 'ionicRadius'],
isUnsupported: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test'
},
cell: {
'@desc': 'crystallographic unit cell, expressed either in lattice integer notation (111-999) or as a coordinate in ijk space, where {1 1 1} is the same as 555. ANDing two cells, for example select cell=555 and cell=556, selects the atoms on the common face. (Note: in the specifc case of CELL, only "=" is allowed as a comparator.)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
configuration: {
'@desc': 'Only in the context {configuration=n}, this option selects the set of atoms with either no ALTLOC specified or those atoms having this index into the array of altlocs within its model. So, for example, if the model has altloc "A" and "B", select configuration=1 is equivalent to select altloc="" or altloc="A", and print {configuration=2} is equivalent to print {altloc="" or altloc="B"}. Configuration 0 is "all atoms in a model having configurations", and an invalid configuration number gives no atoms. (Note: in the specifc case of CONFIGURATION, only "=" is allowed as a comparator.)',
'@examples': [''],
isUnsupported: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test'
},
chain: {
'@desc': 'protein chain. For newer CIF files allowing multicharacter chain specifications, use quotations marks: select chain="AA". For these multicharacter desigations, case is not checked unless the CIF file has lower-case chain designations.',
'@examples': ['chain = A', 'chain = "AA"'],
regex: /[a-zA-Z0-9]+/, map: str,
level: 'chain-test', property: B.ammp('auth_asym_id')
},
chainNo: {
'@desc': 'chain number; sequentially counted from 1 for each model; chainNo == 0 means"no chain" or PDB chain identifier indicated as a blank (Jmol 14.0).',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
color: {
'@desc': 'the atom color',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
covalentRadius: {
'@desc': 'covalent bonding radius, synonymous with covalent. Not used by Jmol, but could be used, for example, in {*}.spacefill={*}.covalentRadius.all.',
'@examples': [''],
abbr: ['covalent'],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
cs: {
'@desc': 'chemical shift calculated using computational results that include magnetic shielding tensors.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
element: {
'@desc': 'element symbol. The value of this parameter depends upon the context. Used with select structure=x, x can be either the quoted element symbol, "H", "He", "Li", etc. or atomic number. In all other contexts, the value is the element symbol. When the atom is a specific isotope, the string will contain the isotope number -- "13C", for example.',
'@examples': ['element=Fe'],
regex: /[a-zA-Z]+/, map: x => B.es(x),
level: 'atom-test', property: B.acp('elementSymbol')
},
elemno: {
'@desc': 'atomic element number',
'@examples': ['elemno=8'],
regex: /[0-9\s{}-]+/, map: x => parseInt(x),
level: 'atom-test', property: B.acp('atomicNumber')
},
eta: {
'@desc': 'Based on Carlos M. Duarte, Leven M. Wadley, and Anna Marie Pyle, RNA structure comparison, motif search and discovery using a reduced representation of RNA conformational space, Nucleic Acids Research, 2003, Vol. 31, No. 16 4755-4761. The parameter eta is the C4\'[i-1]-P[i]-C4\'[i]-P[i+1] dihedral angle; theta is the P[i]-C4\'[i]-P[i+1]-C4\'[i+1] dihedral angle. Both are measured on a 0-360 degree scale because they are commonly near 180 degrees. Using the commands plot PROPERTIES eta theta resno; select visible;wireframe only one can create these authors\' "RNA worm" graph.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
theta: {
'@desc': 'Based on Carlos M. Duarte, Leven M. Wadley, and Anna Marie Pyle, RNA structure comparison, motif search and discovery using a reduced representation of RNA conformational space, Nucleic Acids Research, 2003, Vol. 31, No. 16 4755-4761. The parameter eta is the C4\'[i-1]-P[i]-C4\'[i]-P[i+1] dihedral angle; theta is the P[i]-C4\'[i]-P[i+1]-C4\'[i+1] dihedral angle. Both are measured on a 0-360 degree scale because they are commonly near 180 degrees. Using the commands plot PROPERTIES eta theta resno; select visible;wireframe only one can create these authors\' "RNA worm" graph.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
file: {
'@desc': 'file number containing this atom',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
formalCharge: {
'@desc': 'formal charge',
'@examples': ['formalCharge=1'],
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('pdbx_formal_charge')
},
format: {
'@desc': 'format (label) of the atom.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fXyz: {
'@desc': 'fractional XYZ coordinates',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fX: {
'@desc': 'fractional X coordinate',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fY: {
'@desc': 'fractional Y coordinate',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fZ: {
'@desc': 'fractional Z coordinate',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fuxyz: {
'@desc': 'fractional XYZ coordinates in the unitcell coordinate system',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fux: {
'@desc': 'fractional X coordinate in the unitcell coordinate system',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fuy: {
'@desc': 'fractional Y coordinate in the unitcell coordinate system',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
fuz: {
'@desc': 'fractional Z coordinate in the unit cell coordinate system',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
group: {
'@desc': '3-letter residue code',
'@examples': ['group = ALA'],
regex: /[a-zA-Z0-9]{1,3}/, map: str,
level: 'residue-test', property: B.ammp('label_comp_id')
},
group1: {
'@desc': 'single-letter residue code (amino acids only)',
'@examples': ['group1 = G'],
regex: /[a-zA-Z]/, map: str,
level: 'residue-test', property: B.ammp('label_comp_id')
},
groupID: {
'@desc': 'group ID number: A unique ID for each amino acid or nucleic acid residue in a PDB file. 0 noGroup 1-5 ALA, ARG, ASN, ASP, CYS 6-10 GLN, GLU, GLY, HIS, ILE 11-15 LEU, LYS, MET, PHE, PRO 16-20 SER, THR, TRP, TYR, VAL 21-23 ASX, GLX, UNK 24-29 A, +A, G, +G, I, +I 30-35 C, +C, T, +T, U, +U Additional unique numbers are assigned arbitrarily by Jmol and cannot be used reproducibly.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
groupindex: {
'@desc': 'overall group index',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
hydrophobicity: {
'@desc': 'Aminoacid residue scale of hydrophobicity based on Rose, G. D., Geselowitz, A. R., Lesser, G. J., Lee, R. H., and Zehfus, M. H. (1985). Hydrophobicity of amino acid residues in globular proteins, Science, 229(4716):834-838.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
identify: {
'@desc': 'for a PDB/mmCIF file, a label such as [ILE]7^1:A.CD1%A/3 #47, which includes the group ([ILE]), residue number with optional insertion code (7^1), chain (:A), atom name (CD1), alternate location if present (%A), PDB model number (/3, for NMR models when one file is loaded; /file.model such as /2.3 if more than one file is loaded), and atom number (#47). For non-PDB data, the information is shorter -- for example, H15/2.1 #6, indicating atom name (H15), full file.model number (/2.1), and atom number (#6). If only a single model is loaded, %[identify] does not include the model number.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
insertion: {
'@desc': 'protein residue insertion code',
'@examples': ['insertion=A'],
regex: /[a-zA-Z0-9]/, map: str,
level: 'atom-test', property: B.ammp('pdbx_PDB_ins_code')
},
label: {
'@desc': 'current atom label (same as format)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
mass: {
'@desc': 'atomic mass -- especially useful with appended .max or .sum',
'@examples': ['mass > 13'],
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('mass')
},
model: {
'@desc': 'model number',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
modelindex: {
'@desc': 'a unique number for each model, starting with 0 and spanning all models in all files',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
modO: {
'@desc': 'currently calculated occupancy from modulation (0 to 100; NaN if atom has no occupancy modulation)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
modXYZ: {
'@desc': 'currently calculated displacement modulation (for incommensurately modulated structures). Also modX, modY, modZ for individual components. For atoms without modultion, {xx}.modXYZ is -1 and {xx}.modX is NaN, and in a label %[modXYZ] and %[modX] are blank.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
molecule: {
'@desc': 'molecule number',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
monomer: {
'@desc': 'monomer number (group number) in a polymer (usually a chain), starting with 1, or 0 if not part of a biopolymer -- that is, not a connected carbohydrate, amino acid, or nucleic acid (Jmol 14.3.15)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
ms: {
'@desc': 'magnetic shielding calculated from file-loaded tensors.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
occupancy: {
'@desc': 'CIF file site occupancy. In SELECT command comparisons ("select occupancy < 90"), an integer n implies measurement on a 0-100 scale; also, in the context %[occupancy] or %q for a label, the reported number is a percentage. In all other cases, such as when %Q is used in a label or when a decimal number is used in a comparison, the scale is 0.0 - 1.0.',
'@examples': ['occupancy < 1'],
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('occupancy')
},
partialCharge: {
'@desc': 'partial charge',
'@examples': [''],
isUnsupported: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test'
},
phi: {
'@desc': 'protein group PHI angle for atom\'s residue',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
polymer: {
'@desc': 'sequential polymer number in a model, starting with 1.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
polymerLength: {
'@desc': 'polymer length',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
property_xx: {
'@desc': 'a property created using the DATA command',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
psi: {
'@desc': 'protein group PSI angle for the atom\'s residue',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
radius: {
'@desc': 'currently displayed radius -- In SELECT command comparisons ("select radius=n"), integer n implies Rasmol units 1/250 Angstroms; in all other cases or when a decimal number is used, the units are Angstroms.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
resno: {
'@desc': 'PDB residue number, not including insertion code (see also seqcode, below)',
'@examples': ['resno = 100'],
regex: /-?[0-9]+/, map: x => parseInt(x),
level: 'residue-test', property: B.ammp('auth_seq_id')
},
selected: {
'@desc': '1.0 if atom is selected; 0.0 if not',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
sequence: {
'@desc': 'PDB one-character sequence code, as a string of characters, with "?" indicated where single-character codes are not available',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
seqcode: {
'@desc': 'PDB residue number, including insertion code (for example, 234^2; "seqcode" option added in Jmol 14.3.16)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
seqid: {
'@desc': '(mmCIF only) the value from _atom_site.label_seq_id; a pointer to _entity_poly_seq.num in the ENTITY_POLY_SEQ category specifying the sequence of monomers in a polymer. Allowance is made for the possibility of microheterogeneity in a sample by allowing a given sequence number to be correlated with more than one monomer id. (Jmol 14.2.3)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
shape: {
'@desc': 'hybridization geometry such as "tetrahedral"',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
site: {
'@desc': 'crystallographic site number',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
spacefill: {
'@desc': 'currently displayed radius',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
straightness: {
'@desc': 'quaternion-derived straightness (second derivative of the quaternion describing the orientation of the residue. This quantity will have different values depending upon the setting of quaternionFrame as "A" (alpha-carbon/phosphorus atom only), "C" (alpha-carbon/pyrimidine or purine base based), "P" (carbonyl-carbon peptide plane/phosphorus tetrahedron based), or "N" (amide-nitrogen based). The default is alpha-carbon based, which corresponds closely to the following combination of Ramachandran angles involving three consecutive residues i-1, i, and i+1: -psii-1 - phii + psii + phii+1.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
strucno: {
'@desc': 'a unique number for each helix, sheet, or turn in a model, starting with 1.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
structure: {
'@desc': 'The value of this parameter depends upon the context. Used with select structure=x, x can be either the quoted keyword "none", "turn", "sheet", "helix", "dna", "rna", or "carbohydrate" or a respective number 0-6. In the context {*}.structure, the return value is a number; in the context label %[structure], the return is one of the six keywords.',
'@examples': ['structure="helix"', 'structure=3'],
regex: /none|turn|sheet|helix|dna|rna|carbohydrate|[0-6]/i, map: str,
level: 'residue-test', property: 'structure'
},
substructure: {
'@desc': 'like structure, the value of this parameter depends upon the context. Used with select substructure=x, x can be either the quoted keyword "none", "turn", "sheet", "helix", "dna", "rna", "carbohydrate", "helix310", "helixalpha", or "helixpi", or the respective number 0-9. In the context {*}.substructure, the return value is a number; in the context label %[substructure], the return is one of the nine keywords.',
'@examples': ['substructure = "alphahelix"', 'substructure =9'],
regex: /none|turn|sheet|helix|dna|rna|carbohydrate|helix310|helixalpha|helixpi|[0-9]/i, map: str,
level: 'residue-test', property: 'structure'
},
surfacedistance: {
'@desc': 'A value related to the distance of an atom to a nominal molecular surface. 0 indicates at the surface. Positive numbers are minimum distances in Angstroms from the given atom to the surface.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
symop: {
'@desc': 'the first symmetry operation code that generated this atom by Jmol; an integer starting with 1. See also symmetry, below. This operator is only present if the file contains space group information and the file was loaded using the {i, j, k} option so as to generate symmetry-based atoms. To select only the original atoms prior to application of symmetry, you can either use "SYMOP=n", where n is the symmetry operator corresponding to "x,y,z", or you can specify instead simply "NOT symmetry" the way you might specify "NOT hydrogen". Note that atoms in special positions will have multiple operator matches. These atoms can be selected using the keyword SPECIALPOSITION. The special form select SYMOP=nijk selects a specific translation of atoms from the given crystallographic symmetry operation. Comparators <, <=, >, >=, and != can be used and only apply to the ijk part of the designation. The ijk are relative, not absolute. Thus, symop=2555 selects for atoms that have been transformed by symop=2 but not subjected to any further translation. select symop=1555 is identical to select not symmetry. All other ijk are relative to these selections for 555. If the model was loaded using load "filename.cif" {444 666 1}, where the 1 indicates that all symmetry-generated atoms are to be packed within cell 555 and then translated to fill the other 26 specified cells, then select symop=3555 is nearly the same as select symop=3 and cell=555. (The difference being that cell=555 selects for all atoms that are on any edge of the cell, while symop=3555 does not.) However, the situation is different if instead the model was loaded using load "filename.cif" {444 666 0}, where the 0 indicates that symmetry-generated atoms are to be placed exactly where their symmetry operator would put them (x,-y,z being different then from x, 1-y, z). In that case, select symop=3555 is for all atoms that have been generated using symmetry operation 3 but have not had any additional translations applied to the x,y,z expression found in the CIF file. If, for example, symmetry operation 3 is -x,-y,-z, then load "filename.cif" {444 666 0} will place an atom originally at {1/2, 1/2, 1/2} at positions {-1/2, -1/2, -1/2} (symop=3555) and {-3/2, -3/2, -3/2} (symop=3444) and 24 other sites.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
symmetry: {
'@desc': 'as "symmetry" or in a label as lower-case "o" gives list of crystallographic symmetry operators generating this atom with lattice designations,such as 3555; upper-case "%O" in a label gives a list without the lattice designations. See also symop, above.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
temperature: {
'@desc': 'yes yes temperature factor (B-factor)',
'@examples': ['temperature >= 20'],
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('B_iso_or_equiv')
},
unitXyz: {
'@desc': 'unit cell XYZ coordinates',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
uX: {
'@desc': 'unit cell X coordinate normalized to [0,1)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
uY: {
'@desc': 'unit cell Y coordinate normalized to [0,1)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
uZ: {
'@desc': 'unit cell Z coordinate normalized to [0,1)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
valence: {
'@desc': 'the valence of an atom (sum of bonds, where double bond counts as 2 and triple bond counts as 3',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
vanderwaals: {
'@desc': 'van der Waals radius',
'@examples': ['vanderwaals >2'],
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('vdw')
},
vectorScale: {
'@desc': 'vibration vector scale',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
volume: {
'@desc': 'approximate van der Waals volume for this atom. Note, {*}.volume gives an average; use {*}.volume.sum to get total volume.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
vXyz: {
'@desc': 'vibration vector, or individual components as %vx %vy %vz. For atoms without vibration vectors, {xx}.vXyz is -1; in a label, %[vxyz] is blank.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
vX: {
'@desc': 'vibration vector X coordinate; for atoms without vibration vector, {xx}.vX is NaN (same for vY and vZ)',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
vY: {
'@desc': 'vibration vector Y coordinate',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
vZ: {
'@desc': 'vibration vector Z coordinate',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
xyz: {
'@desc': 'Cartesian XYZ coordinates; select xyz > 1.0 selects atoms more than one Angstrom from the origin.',
'@examples': [''],
isUnsupported: true,
regex: /[0-9\s{}-]+/, map: str,
level: 'atom-test'
},
};

View File

@@ -1,35 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*
* Adapted from MolQL project
*/
import { properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
export const Properties: string[] = [];
for (const name in properties) {
if (properties[name].isUnsupported) continue;
Properties.push(name);
if (properties[name].abbr) Properties.push(...properties[name].abbr!);
}
export const Operators: string[] = [];
operators.forEach(o => {
if (o.isUnsupported) return;
Operators.push(o.name);
if (o.abbr) Operators.push(...o.abbr);
});
export const Keywords: string[] = [];
for (const name in keywords) {
if (!keywords[name].map) continue;
Keywords.push(name);
if (keywords[name].abbr) Keywords.push(...keywords[name].abbr!);
}
export const _all = { Properties, Operators, Keywords };

View File

@@ -1,56 +0,0 @@
/**
* Copyright (c) 2017-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>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
export const examples = [{
name: 'ALA residues',
value: 'resn ALA'
}, {
name: 'Atoms named "C", "O", "N", or "CA"',
value: 'name c+o+n+ca'
}, {
name: 'Residues with helix or sheet secondary structure',
value: 'ss h+s'
}, {
name: 'C-alpha atoms of residues 100 to 180 in chain A',
value: 'A/100-180/CA'
}, {
name: 'Residues 100 to 180',
value: 'resi 100-180'
}, {
name: 'Atoms that are 1 ang + vdw radius away from polymer',
value: 'polymer gap 1'
}, {
name: 'Residues within 4 ang of HEM',
value: 'byres resn HEM around 4'
}, {
name: 'HEM and residues within 4 ang',
value: 'byres resn HEM expand 4'
}, {
name: 'Solvent close (2.5 ang) to polymer',
value: 'solvent NEAR_TO 2.5 OF polymer'
}, {
name: 'Cystein residues within 3 ang of HEM',
value: 'byres resn CYS WITHIN 3 OF resn HEM'
}, {
name: 'Solvent atoms 4 ang away from oxygen',
value: 'solvent beyond 4 of (name O and not solvent)'
}, {
name: 'All rings in PHE',
value: 'byring resn PHE'
}, {
name: 'CYS and all bound residues',
value: 'byres BOUND_TO resn CYS'
}, {
name: 'HEM and atoms up to 7 bonds away',
value: 'resn HEM extend 7'
}, {
name: 'Atoms with alternate location A or none',
value: 'alt A+""'
}];

View File

@@ -1,275 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import * as h from '../helper';
import { KeywordDict } from '../types';
const ResDict = {
nucleic: ['A', 'C', 'T', 'G', 'U', 'DA', 'DC', 'DT', 'DG', 'DU'],
protein: ['ALA', 'ARG', 'ASN', 'ASP', 'CYS', 'CYX', 'GLN', 'GLU', 'GLY', 'HIS', 'HID', 'HIE', 'HIP', 'ILE', 'LEU', 'LYS', 'MET', 'MSE', 'PHE', 'PRO', 'SER', 'THR', 'TRP', 'TYR', 'VAL'],
solvent: ['HOH', 'WAT', 'H20', 'TIP', 'SOL']
};
const Backbone = {
nucleic: ['P', "O3'", "O5'", "C5'", "C4'", "C3'", 'OP1', 'OP2', 'O3*', 'O5*', 'C5*', 'C4*', 'C3*',
"C2'", "C1'", "O4'", "O2'"],
protein: ['C', 'N', 'CA', 'O']
};
function backboneExpr() {
return B.struct.combinator.merge([
B.struct.modifier.intersectBy({
0: B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.protein),
B.ammp('label_comp_id')
])
}),
by: B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.core.type.set(Backbone.protein),
B.ammp('label_atom_id')
])
})
}),
B.struct.modifier.intersectBy({
0: B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.nucleic),
B.ammp('label_comp_id')
])
}),
by: B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
B.core.type.set(Backbone.nucleic),
B.ammp('label_atom_id')
])
})
}),
]);
}
export const keywords: KeywordDict = {
all: {
'@desc': 'All atoms currently loaded into PyMOL',
abbr: ['*'],
map: () => B.struct.generator.all()
},
none: {
'@desc': 'No atoms (empty selection)',
map: () => B.struct.generator.empty()
},
hydrogens: {
'@desc': 'All hydrogen atoms currently loaded into PyMOL',
abbr: ['hydro', 'h.'],
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.eq([
B.acp('elementSymbol'),
B.es('H')
])
})
},
hetatm: {
'@desc': 'All atoms loaded from Protein Data Bank HETATM records',
abbr: ['het'],
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.eq([B.ammp('isHet'), true])
})
},
visible: {
'@desc': 'All atoms in enabled objects with at least one visible representation',
abbr: ['v.']
},
polymer: {
'@desc': 'All atoms on the polymer (not het). Finds atoms with residue identifiers matching a known polymer, such a peptide and DNA.',
abbr: ['pol.'],
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.nucleic.concat(ResDict.protein)),
B.ammp('label_comp_id')
])
})
},
sidechain: {
'@desc': 'Polymer non-backbone atoms (new in PyMOL 1.6.1)',
abbr: ['sc.'],
map: () => {
return B.struct.modifier.exceptBy({
'0': B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.nucleic.concat(ResDict.protein)),
B.ammp('label_comp_id')
])
}),
by: backboneExpr()
});
},
},
present: {
'@desc': 'All atoms with defined coordinates in the current state (used in creating movies)',
abbr: ['pr.']
},
center: {
'@desc': 'Pseudo-atom at the center of the scene'
},
origin: {
'@desc': 'Pseudo-atom at the origin of rotation',
},
enabled: {
'@desc': 'All enabled objects or selections from the object list.',
},
masked: {
'@desc': 'All masked atoms.',
abbr: ['msk.']
},
protected: {
'@desc': 'All protected atoms.',
abbr: ['pr.']
},
bonded: {
'@desc': 'All bonded atoms',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.rel.gr([B.struct.atomProperty.core.bondCount({
flags: B.struct.type.bondFlags(['covalent', 'metallic', 'sulfide'])
}), 0])
})
},
donors: {
'@desc': 'All hydrogen bond donor atoms.',
abbr: ['don.']
},
acceptors: {
'@desc': 'All hydrogen bond acceptor atoms.',
abbr: ['acc.']
},
fixed: {
'@desc': 'All fixed atoms.',
abbr: ['fxd.']
},
restrained: {
'@desc': 'All restrained atoms.',
abbr: ['rst.']
},
organic: {
'@desc': 'All atoms in non-polymer organic compounds (e.g. ligands, buffers). Finds carbon-containing molecules that do not match known polymers.',
abbr: ['org.'],
map: () => h.asAtoms(B.struct.modifier.expandProperty({
'0': B.struct.modifier.union([
B.struct.generator.queryInSelection({
'0': B.struct.generator.atomGroups({
'residue-test': B.core.logic.not([
B.core.set.has([
B.core.type.set(ResDict.nucleic.concat(ResDict.protein)),
B.ammp('label_comp_id')
])
])
}),
query: B.struct.generator.atomGroups({
'atom-test': B.core.rel.eq([
B.es('C'),
B.acp('elementSymbol')
])
})
})
]),
property: B.ammp('residueKey')
}))
},
inorganic: {
'@desc': 'All non-polymer inorganic atoms/ions. Finds atoms in molecules that do not contain carbon and do not match any known solvent residues.',
abbr: ['ino.'],
map: () => h.asAtoms(B.struct.modifier.expandProperty({
'0': B.struct.modifier.union([
B.struct.filter.pick({
'0': B.struct.generator.atomGroups({
'residue-test': B.core.logic.not([
B.core.set.has([
B.core.type.set(ResDict.nucleic.concat(ResDict.protein).concat(ResDict.solvent)),
B.ammp('label_comp_id')
])
]),
'group-by': B.ammp('residueKey')
}),
test: B.core.logic.not([
B.core.set.has([
B.struct.atomSet.propertySet([B.acp('elementSymbol')]),
B.es('C')
])
])
})
]),
property: B.ammp('residueKey')
}))
},
solvent: {
'@desc': 'All water molecules. The hardcoded solvent residue identifiers are currently: HOH, WAT, H20, TIP, SOL.',
abbr: ['sol.'],
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.solvent),
B.ammp('label_comp_id')
])
})
},
guide: {
'@desc': 'All protein CA and nucleic acid C4*/C4',
map: () => B.struct.combinator.merge([
B.struct.generator.atomGroups({
'atom-test': B.core.rel.eq([
B.atomName('CA'),
B.ammp('label_atom_id')
]),
'residue-test': B.core.set.has([
B.core.type.set(ResDict.protein),
B.ammp('label_comp_id')
])
}),
B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
h.atomNameSet(['C4*', 'C4\'']),
B.ammp('label_atom_id')
]),
'residue-test': B.core.set.has([
B.core.type.set(ResDict.nucleic),
B.ammp('label_comp_id')
])
})
]),
},
metals: {
'@desc': 'All metal atoms (new in PyMOL 1.6.1)'
},
backbone: {
'@desc': 'Polymer backbone atoms (new in PyMOL 1.6.1)',
abbr: ['bb.'],
map: () => backboneExpr()
},
'polymer.protein': {
'@desc': 'Protein (New in PyMOL 2.1)',
abbr: ['polymer.protein'],
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.protein),
B.ammp('label_comp_id')
])
})
},
'polymer.nucleic': {
'@desc': 'Nucleic Acid (New in PyMOL 2.1)',
abbr: ['polymer.nucleic'],
map: () => B.struct.generator.atomGroups({
'residue-test': B.core.set.has([
B.core.type.set(ResDict.nucleic),
B.ammp('label_comp_id')
])
})
}
};

View File

@@ -1,63 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
const _docs: string[] = [
'PyMol',
'============',
'--------------------------------',
''
];
_docs.push(`## Properties\n\n`);
_docs.push('--------------------------------\n');
for (const name in properties) {
if (properties[name].isUnsupported) continue;
const names = [name];
if (properties[name].abbr) names.push(...properties[name].abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (properties[name]['@desc']) {
_docs.push(`*${properties[name]['@desc']}*\n`);
}
}
_docs.push(`## Operators\n\n`);
_docs.push('--------------------------------\n');
operators.forEach(o => {
if (o.isUnsupported) return;
const names = [o.name];
if (o.abbr) names.push(...o.abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (o['@desc']) {
_docs.push(`*${o['@desc']}*\n`);
}
});
_docs.push(`## Keywords\n\n`);
_docs.push('--------------------------------\n');
for (const name in keywords) {
if (!keywords[name].map) continue;
const names = [name];
if (keywords[name].abbr) names.push(...keywords[name].abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (keywords[name]['@desc']) {
_docs.push(`*${keywords[name]['@desc']}*\n`);
}
}
export const docs = _docs.join('\n');

View File

@@ -1,371 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import * as P from '../../../mol-util/monadic-parser';
import * as h from '../helper';
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { OperatorList } from '../types';
import { Expression } from '../../language/expression';
export const operators: OperatorList = [
{
'@desc': 'Selects atoms that are not included in s1.',
'@examples': [
'NOT resn ALA',
'not (resi 42 or chain A)',
'!resi 42 or chain A',
],
name: 'not',
type: h.prefix,
rule: P.MonadicParser.alt(
P.MonadicParser.regexp(/NOT/i).skip(P.MonadicParser.whitespace),
P.MonadicParser.string('!').skip(P.MonadicParser.optWhitespace)
),
map: (op, selection) => h.invertExpr(selection),
},
{
'@desc': 'Selects atoms included in both s1 and s2.',
'@examples': ['chain A AND name CA'],
name: 'and',
type: h.binaryLeft,
rule: h.infixOp(/AND|&/i),
map: (op, selection, by) =>
B.struct.modifier.intersectBy({ 0: selection, by }),
},
{
'@desc': 'Selects atoms included in either s1 or s2.',
'@examples': ['chain A OR chain B'],
name: 'or',
type: h.binaryLeft,
rule: h.infixOp(/OR|\|/i),
map: (op: string, s1: Expression, s2: Expression) => B.struct.combinator.merge([s1, s2]),
},
{
'@desc':
'Selects atoms in s1 whose identifiers name, resi, resn, chain and segi all match atoms in s2.',
'@examples': ['chain A IN chain B'],
name: 'in',
type: h.binaryLeft,
rule: h.infixOp(/IN/i),
map: (op: string, selection: Expression, source: Expression) => {
return B.struct.filter.withSameAtomProperties({
0: selection,
source,
property: B.core.type.compositeKey([
B.ammp('label_atom_id'),
B.ammp('label_seq_id'),
B.ammp('label_comp_id'),
B.ammp('auth_asym_id'),
B.ammp('label_asym_id'),
]),
});
},
},
{
'@desc':
'Selects atoms in s1 whose identifiers name and resi match atoms in s2.',
'@examples': ['chain A LIKE chain B'],
name: 'like',
type: h.binaryLeft,
rule: h.infixOp(/LIKE|l\./i),
map: (op: string, selection: Expression, source: Expression) => {
return B.struct.filter.withSameAtomProperties({
0: selection,
source,
property: B.core.type.compositeKey([
B.ammp('label_atom_id'),
B.ammp('label_seq_id'),
]),
});
},
},
{
'@desc':
'Selects all atoms whose van der Waals radii are separated from the van der Waals radii of s1 by a minimum of X Angstroms.',
'@examples': ['solvent GAP 2'],
name: 'gap',
type: h.postfix,
rule: h
.postfixOp(/GAP\s+([-+]?[0-9]*\.?[0-9]+)/i, 1)
.map((x: any) => parseFloat(x)),
map: (distance: number, target: Expression) => {
return B.struct.filter.within({
'0': B.struct.generator.all(),
target,
'atom-radius': B.acp('vdw'),
'max-radius': distance,
invert: true,
});
},
},
{
'@desc':
'Selects atoms with centers within X Angstroms of the center of any atom in s1.',
'@examples': ['resname LIG AROUND 1'],
name: 'around',
abbr: ['a.'],
type: h.postfix,
rule: h
.postfixOp(/(AROUND|a\.)\s+([-+]?[0-9]*\.?[0-9]+)/i, 2)
.map((x: any) => parseFloat(x)),
map: (radius: number, target: Expression) => {
return B.struct.modifier.exceptBy({
'0': B.struct.filter.within({
'0': B.struct.generator.all(),
target,
'max-radius': radius,
}),
by: target,
});
},
},
{
'@desc':
'Expands s1 by all atoms within X Angstroms of the center of any atom in s1.',
'@examples': ['chain A EXPAND 3'],
name: 'expand',
abbr: ['x.'],
type: h.postfix,
rule: h
.postfixOp(/(EXPAND|x\.)\s+([-+]?[0-9]*\.?[0-9]+)/i, 2)
.map((x: any) => parseFloat(x)),
map: (radius: number, selection: Expression) => {
return B.struct.modifier.includeSurroundings({ 0: selection, radius });
},
},
{
'@desc':
'Selects atoms in s1 that are within X Angstroms of any atom in s2.',
'@examples': ['chain A WITHIN 3 OF chain B'],
name: 'within',
abbr: ['w.'],
type: h.binaryLeft,
rule: h.ofOp('WITHIN', 'w.'),
map: (radius: number, selection: Expression, target: Expression) => {
return B.struct.filter.within({
0: selection,
target,
'max-radius': radius,
});
},
},
{
'@desc':
'Same as within, but excludes s2 from the selection (and thus is identical to s1 and s2 around X).',
'@examples': ['chain A NEAR_TO 3 OF chain B'],
name: 'near_to',
abbr: ['nto.'],
type: h.binaryLeft,
rule: h.ofOp('NEAR_TO', 'nto.'),
map: (radius: number, selection: Expression, target: Expression) => {
return B.struct.modifier.exceptBy({
'0': B.struct.filter.within({
'0': selection,
target,
'max-radius': radius,
}),
by: target,
});
},
},
{
'@desc': 'Selects atoms in s1 that are at least X Anstroms away from s2.',
'@examples': ['solvent BEYOND 2 OF chain A'],
name: 'beyond',
abbr: ['be.'],
type: h.binaryLeft,
rule: h.ofOp('BEYOND', 'be.'),
map: (radius: number, selection: Expression, target: Expression) => {
return B.struct.modifier.exceptBy({
'0': B.struct.filter.within({
'0': selection,
target,
'max-radius': radius,
invert: true,
}),
by: target,
});
},
},
{
'@desc': 'Expands selection to complete residues.',
'@examples': ['BYRESIDUE name N'],
name: 'byresidue',
abbr: ['byresi', 'byres', 'br.'],
type: h.prefix,
rule: h.prefixOp(/BYRESIDUE|byresi|byres|br\./i),
map: (op: string, selection: Expression) => {
return h.asAtoms(
B.struct.modifier.expandProperty({
'0': B.struct.modifier.union({ 0: selection }),
property: B.ammp('residueKey'),
})
);
},
},
{
'@desc':
'Completely selects all alpha carbons in all residues covered by a selection.',
'@examples': ['BYCALPHA chain A'],
name: 'bycalpha',
abbr: ['bca.'],
type: h.prefix,
rule: h.prefixOp(/BYCALPHA|bca\./i),
map: (op: string, selection: Expression) => {
return B.struct.generator.queryInSelection({
'0': B.struct.modifier.expandProperty({
'0': B.struct.modifier.union({ 0: selection }),
property: B.ammp('residueKey'),
}),
query: B.struct.generator.atomGroups({
'atom-test': B.core.rel.eq([
B.atomName('CA'),
B.ammp('label_atom_id'),
]),
}),
});
},
},
{
'@desc': 'Expands selection to complete molecules.',
'@examples': ['BYMOLECULE resi 20-30'],
name: 'bymolecule',
isUnsupported: true, // structure-query.atom-property.topology.connected-component-key' is not implemented
abbr: ['bymol', 'bm.'],
type: h.prefix,
rule: h.prefixOp(/BYMOLECULE|bymol|bm\./i),
map: (op: string, selection: Expression) => {
return h.asAtoms(
B.struct.modifier.expandProperty({
'0': B.struct.modifier.union({ 0: selection }),
property: B.atp('connectedComponentKey'),
})
);
},
},
{
'@desc': 'Expands selection to complete fragments.',
'@examples': ['BYFRAGMENT resi 10'],
name: 'byfragment',
abbr: ['byfrag', 'bf.'],
isUnsupported: true,
type: h.prefix,
rule: h.prefixOp(/BYFRAGMENT|byfrag|bf\./i),
map: (op: string, selection: Expression) => [op, selection],
},
{
'@desc': 'Expands selection to complete segments.',
'@examples': ['BYSEGMENT resn CYS'],
name: 'bysegment',
abbr: ['bysegi', 'byseg', 'bs.'],
type: h.prefix,
rule: h.prefixOp(/BYSEGMENT|bysegi|byseg|bs\./i),
map: (op: string, selection: Expression) => {
return h.asAtoms(
B.struct.modifier.expandProperty({
'0': B.struct.modifier.union({ 0: selection }),
property: B.ammp('chainKey'),
})
);
},
},
{
'@desc': 'Expands selection to complete objects.',
'@examples': ['BYOBJECT chain A'],
name: 'byobject',
abbr: ['byobj', 'bo.'],
isUnsupported: true,
type: h.prefix,
rule: h.prefixOp(/BYOBJECT|byobj|bo\./i),
map: (op: string, selection: Expression) => [op, selection],
},
{
'@desc': 'Expands selection to unit cell.',
'@examples': ['BYCELL chain A'],
name: 'bycell',
isUnsupported: true,
type: h.prefix,
rule: h.prefixOp(/BYCELL/i),
map: (op: string, selection: Expression) => [op, selection],
},
{
'@desc': 'All rings of size ≤ 7 which have at least one atom in s1.',
'@examples': ['BYRING resn HEM'],
name: 'byring',
// isUnsupported: true, // structure-query.atom-set.atom-count' is not implemented.
type: h.prefix,
rule: h.prefixOp(/BYRING/i),
map: (op: string, selection: Expression) => {
return h.asAtoms(
B.struct.modifier.intersectBy({
'0': B.struct.filter.pick({
'0': B.struct.generator.rings(),
test: B.core.logic.and([
B.core.rel.lte([B.struct.atomSet.atomCount(), 7]),
B.core.rel.gr([B.struct.atomSet.countQuery([selection]), 1]),
]),
}),
by: selection,
})
);
},
},
{
'@desc': 'Selects atoms directly bonded to s1, excludes s1.',
'@examples': ['NEIGHBOR resn CYS'],
name: 'neighbor',
type: h.prefix,
abbr: ['nbr.'],
rule: h.prefixOp(/NEIGHBOR|nbr\./i),
map: (op: string, selection: Expression) => {
return B.struct.modifier.exceptBy({
'0': h.asAtoms(
B.struct.modifier.includeConnected({
'0': B.struct.modifier.union({ 0: selection }),
'bond-test': true,
})
),
by: selection,
});
},
},
{
'@desc': 'Selects atoms directly bonded to s1, may include s1.',
'@examples': ['BOUND_TO name CA'],
name: 'bound_to',
abbr: ['bto.'],
type: h.prefix,
rule: h.prefixOp(/BOUND_TO|bto\./i),
map: (op: string, selection: Expression) => {
return h.asAtoms(
B.struct.modifier.includeConnected({
'0': B.struct.modifier.union({ 0: selection }),
})
);
},
},
{
'@desc': 'Extends s1 by X bonds connected to atoms in s1.',
'@examples': ['resname LIG EXTEND 3'],
name: 'extend',
abbr: ['xt.'],
type: h.postfix,
rule: h.postfixOp(/(EXTEND|xt\.)\s+([0-9]+)/i, 2).map((x: any) => parseInt(x)),
map: (count: number, selection: Expression) => {
return h.asAtoms(
B.struct.modifier.includeConnected({
'0': B.struct.modifier.union({ 0: selection }),
'bond-test': true,
'layer-count': count,
})
);
},
},
];

View File

@@ -1,173 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
// https://pymol.org/dokuwiki/doku.php?id=selection:alpha
// https://pymolwiki.org/index.php/Selection_Algebra
// https://github.com/evonove/pymol/blob/master/pymol/layer3/Selector.cpp
import * as P from '../../../mol-util/monadic-parser';
import * as h from '../helper';
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
import { AtomGroupArgs } from '../types';
import { Transpiler } from '../transpiler';
const propertiesDict = h.getPropertyRules(properties);
const slash = P.MonadicParser.string('/');
function orNull(rule: P.MonadicParser<any>) {
return rule.or(P.MonadicParser.of(null));
}
function atomSelectionQuery(x: any) {
const tests: AtomGroupArgs = {};
const props: { [k: string]: any[] } = {};
for (const k in x) {
const ps = properties[k];
if (!ps) {
throw new Error(`property '${k}' not supported, value '${x[k]}'`);
}
if (x[k] === null) continue;
if (!props[ps.level]) props[ps.level] = [];
props[ps.level].push(x[k]);
}
for (const p in props) {
tests[p] = h.andExpr(props[p]);
}
return B.struct.generator.atomGroups(tests);
}
const lang = P.MonadicParser.createLanguage({
Parens: function (r: any) {
return P.MonadicParser.alt(
r.Parens,
r.Operator,
r.Expression
).wrap(P.MonadicParser.string('('), P.MonadicParser.string(')'));
},
Expression: function (r: any) {
return P.MonadicParser.alt(
r.Keywords,
r.AtomSelectionMacro.map(atomSelectionQuery),
r.NamedAtomProperties,
r.Pepseq,
r.Rep,
r.Object
);
},
AtomSelectionMacro: function (r: any) {
return P.MonadicParser.alt(
slash.then(P.MonadicParser.alt(
P.MonadicParser.seq(
orNull(r.ObjectProperty).skip(slash),
orNull(propertiesDict.segi).skip(slash),
orNull(propertiesDict.chain).skip(slash),
orNull(propertiesDict.resi).skip(slash),
orNull(propertiesDict.name)
).map(x => { return { object: x[0], segi: x[1], chain: x[2], resi: x[3], name: x[4] }; }),
P.MonadicParser.seq(
orNull(r.ObjectProperty).skip(slash),
orNull(propertiesDict.segi).skip(slash),
orNull(propertiesDict.chain).skip(slash),
orNull(propertiesDict.resi)
).map(x => { return { object: x[0], segi: x[1], chain: x[2], resi: x[3] }; }),
P.MonadicParser.seq(
orNull(r.ObjectProperty).skip(slash),
orNull(propertiesDict.segi).skip(slash),
orNull(propertiesDict.chain)
).map(x => { return { object: x[0], segi: x[1], chain: x[2] }; }),
P.MonadicParser.seq(
orNull(r.ObjectProperty).skip(slash),
orNull(propertiesDict.segi)
).map(x => { return { object: x[0], segi: x[1] }; }),
P.MonadicParser.seq(
orNull(r.ObjectProperty)
).map(x => { return { object: x[0] }; }),
)),
P.MonadicParser.alt(
P.MonadicParser.seq(
orNull(r.ObjectProperty).skip(slash),
orNull(propertiesDict.segi).skip(slash),
orNull(propertiesDict.chain).skip(slash),
orNull(propertiesDict.resi).skip(slash),
orNull(propertiesDict.name)
).map(x => { return { object: x[0], segi: x[1], chain: x[2], resi: x[3], name: x[4] }; }),
P.MonadicParser.seq(
orNull(propertiesDict.segi).skip(slash),
orNull(propertiesDict.chain).skip(slash),
orNull(propertiesDict.resi).skip(slash),
orNull(propertiesDict.name)
).map(x => { return { segi: x[0], chain: x[1], resi: x[2], name: x[3] }; }),
P.MonadicParser.seq(
orNull(propertiesDict.chain).skip(slash),
orNull(propertiesDict.resi).skip(slash),
orNull(propertiesDict.name)
).map(x => { return { chain: x[0], resi: x[1], name: x[2] }; }),
P.MonadicParser.seq(
orNull(propertiesDict.resi).skip(slash),
orNull(propertiesDict.name)
).map(x => { return { resi: x[0], name: x[1] }; }),
)
);
},
NamedAtomProperties: function () {
return P.MonadicParser.alt(...h.getNamedPropertyRules(properties));
},
Keywords: () => P.MonadicParser.alt(...h.getKeywordRules(keywords)),
ObjectProperty: () => {
const w = h.getReservedWords(properties, keywords, operators)
.sort(h.strLenSortFn).map(h.escapeRegExp).join('|');
return P.MonadicParser.regexp(new RegExp(`(?!(${w}))[A-Z0-9_]+`, 'i'));
},
Object: (r: any) => {
return r.ObjectProperty.notFollowedBy(slash)
.map((x: any) => { throw new Error(`property 'object' not supported, value '${x}'`); });
},
// Selects peptide sequence matching upper-case one-letter
// sequence SEQ (see also FindSeq).
// PEPSEQ seq
Pepseq: () => {
return P.MonadicParser.regexp(/(PEPSEQ|ps\.)\s+([a-z]+)/i, 2)
.map(h.makeError(`operator 'pepseq' not supported`));
},
// Selects atoms which show representation rep.
// REP rep
Rep: () => {
return P.MonadicParser.regexp(/REP\s+(lines|spheres|mesh|ribbon|cartoon|sticks|dots|surface|labels|extent|nonbonded|nb_spheres|slice|extent|slice|dashes|angles|dihedrals|cgo|cell|callback|everything)/i, 1)
.map(h.makeError(`operator 'rep' not supported`));
},
Operator: function (r: any) {
return h.combineOperators(operators, P.MonadicParser.alt(r.Parens, r.Expression, r.Operator));
},
Query: function (r: any) {
return P.MonadicParser.alt(
r.Operator,
r.Parens,
r.Expression
).trim(P.MonadicParser.optWhitespace);
}
});
export const transpiler: Transpiler = str => lang.Query.tryParse(str);

View File

@@ -1,215 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*
* Adapted from MolQL project
*/
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { PropertyDict } from '../types';
const reFloat = /[-+]?[0-9]*\.?[0-9]+/;
function atomNameListMap(x: string) { return x.split('+').map(B.atomName); }
function listMap(x: string) { return x.split('+').map(x => x.replace(/^["']|["']$/g, '')); }
function listOrRangeMap(x: string) {
// cases
if (x.includes('-') && x.includes('+')) {
const pSplit = x.split('+').map(x => x.replace(/^["']|["']$/g, ''));
const res: number[] = [];
pSplit.forEach(x => {
if (x.includes('-') && !x.startsWith('-')) {
const [min, max] = x.split('-').map(x => parseInt(x));
for (let i = min; i <= max; i++) {
res.push(i);
}
} else if (x.includes('-') && x.startsWith('-') && x.match(/[0-9]+-[-0-9]+/)) {
const min = -parseInt(x.split('-')[1]);
let max;
if (x.includes('--')) {
max = -parseInt(x.split('-')[3]);
} else {
max = parseInt(x.split('-')[2]);
}
for (let i = min; i <= max; i++) {
res.push(i);
}
} else if (x.includes('-') && x.startsWith('-') && !x.match(/[0-9]+-[-0-9]+/)) {
res.push(parseInt(x));
} else {
res.push(parseInt(x));
}
});
return res;
} else if (x.includes('-') && !x.includes('+')) {
const res: number[] = [];
if (!x.startsWith('-')) {
const [min, max] = x.split('-').map(x => parseInt(x));
for (let i = min; i <= max; i++) {
res.push(i);
}
} else if (x.startsWith('-') && x.match(/[0-9]+-[-0-9]+/)) {
const min = -parseInt(x.split('-')[1]);
let max;
if (x.includes('--')) {
max = -parseInt(x.split('-')[3]);
} else {
max = parseInt(x.split('-')[2]);
}
for (let i = min; i <= max; i++) {
res.push(i);
}
} else if (x.startsWith('-') && !x.match(/[0-9]+-[-0-9]+/)) {
res.push(parseInt(x));
} else {
res.push(parseInt(x));
}
return res;
} else if (!x.includes('-') && x.includes('+')) {
return listMap(x).map(x => parseInt(x));
} else {
return [parseInt(x)];
}
}
function elementListMap(x: string) {
return x.split('+').map(B.struct.type.elementSymbol);
}
const sstrucDict: { [k: string]: string } = {
H: 'helix',
S: 'beta',
L: 'none'
};
function sstrucListMap(x: string) {
return {
flags: B.struct.type.secondaryStructureFlags(
x.toUpperCase().split('+').map(ss => sstrucDict[ss] || 'none')
)
};
}
export const properties: PropertyDict = {
symbol: {
'@desc': 'chemical-symbol-list: list of 1- or 2-letter chemical symbols from the periodic table',
'@examples': ['symbol O+N'],
abbr: ['e.'], regex: /[a-zA-Z'"+]+/, map: elementListMap,
level: 'atom-test', property: B.acp('elementSymbol')
},
name: {
'@desc': 'atom-name-list: list of up to 4-letter codes for atoms in proteins or nucleic acids',
'@examples': ['name CA+CB+CG+CD'],
abbr: ['n.'], regex: /[a-zA-Z0-9'"+]+/, map: atomNameListMap,
level: 'atom-test', property: B.ammp('label_atom_id')
},
resn: {
'@desc': 'residue-name-list: list of 3-letter codes for amino acids or list of up to 2-letter codes for nucleic acids',
'@examples': ['resn ASP+GLU+ASN+GLN', 'resn A+G'],
abbr: ['resname', 'r.'], regex: /[a-zA-Z0-9'"+]+/, map: listMap,
level: 'residue-test', property: B.ammp('label_comp_id')
},
resi: {
'@desc': 'residue-identifier-list list of up to 4-digit residue numbers or residue-identifier-range',
'@examples': ['resi 1+10+100+1000', 'resi 1-10'],
abbr: ['resident', 'residue', 'resid', 'i.'], regex: /[0-9+-]+/, map: listOrRangeMap,
level: 'residue-test', property: B.ammp('auth_seq_id')
},
alt: {
'@desc': 'alternate-conformation-identifier-list list of single letters',
'@examples': ['alt A+B', 'alt ""', 'alt ""+A'],
abbr: [], regex: /[a-zA-Z0-9'"+]+/, map: listMap,
level: 'atom-test', property: B.ammp('label_alt_id')
},
chain: {
'@desc': 'chain-identifier-list list of single letters or sometimes numbers',
'@examples': ['chain A'],
abbr: ['c.'], regex: /[a-zA-Z0-9'"+]+/, map: listMap,
level: 'chain-test', property: B.ammp('auth_asym_id')
},
segi: {
'@desc': 'segment-identifier-list list of up to 4 letter identifiers',
'@examples': ['segi lig'],
abbr: ['segid', 's.'], regex: /[a-zA-Z0-9'"+]+/, map: listMap,
level: 'chain-test', property: B.ammp('label_asym_id')
},
flag: {
'@desc': 'flag-number a single integer from 0 to 31',
'@examples': ['flag 0'],
isUnsupported: true,
abbr: ['f.'], regex: /[0-9]+/, map: x => parseInt(x),
level: 'atom-test'
},
numeric_type: {
'@desc': 'type-number a single integer',
'@examples': ['nt. 5'],
isUnsupported: true,
abbr: ['nt.'], regex: /[0-9]+/, map: x => parseInt(x),
level: 'atom-test'
},
text_type: {
'@desc': 'type-string a list of up to 4 letter codes',
'@examples': ['text_type HA+HC'],
isUnsupported: true,
abbr: ['tt.'], regex: /[a-zA-Z0-9'"+]+/, map: listMap,
level: 'atom-test'
},
id: {
'@desc': 'external-index-number a single integer',
'@examples': ['id 23'],
regex: /[0-9+-]+/, map: listOrRangeMap,
level: 'atom-test', property: B.ammp('id')
},
index: {
'@desc': 'internal-index-number a single integer',
'@examples': ['index 11'],
regex: /[0-9+-]+/, map: listOrRangeMap,
level: 'atom-test', property: B.ammp('id')
},
ss: {
'@desc': 'secondary-structure-type list of single letters. Helical regions should be assigned H and sheet regions S. Loop regions can either be assigned L or be blank.',
'@examples': ['ss H+S+L', 'ss S+""'],
abbr: [], regex: /[a-zA-Z'"+]+/, map: sstrucListMap,
level: 'residue-test', property: B.ammp('secondaryStructureFlags')
},
b: {
'@desc': 'comparison-operator b-factor-value a real number',
'@examples': ['b > 10'],
isNumeric: true,
abbr: [], regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('B_iso_or_equiv')
},
q: {
'@desc': 'comparison-operator occupancy-value a real number',
'@examples': ['q <0.50'],
isNumeric: true,
abbr: [], regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('occupancy')
},
formal_charge: {
'@desc': 'comparison-operator formal charge-value an integer',
'@examples': ['fc. = -1'],
isNumeric: true,
abbr: ['fc.'], regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('pdbx_formal_charge')
},
partial_charge: {
'@desc': 'comparison-operator partial charge-value a real number',
'@examples': ['pc. > 1'],
isUnsupported: true,
isNumeric: true,
abbr: ['pc.'], regex: reFloat, map: x => parseFloat(x),
level: 'atom-test'
},
elem: {
'@desc': 'str atomic element symbol string ("X" if undefined)',
'@examples': ['elem N'],
regex: /[a-zA-Z0-9]{1,3}/, map: x => B.es(x),
level: 'atom-test', property: B.acp('elementSymbol')
}
};

View File

@@ -1,35 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
export const Properties: string[] = [];
for (const name in properties) {
if (properties[name].isUnsupported) continue;
Properties.push(name);
if (properties[name].abbr) Properties.push(...properties[name].abbr!);
}
export const Operators: string[] = [];
operators.forEach(o => {
if (o.isUnsupported) return;
Operators.push(o.name);
if (o.abbr) Operators.push(...o.abbr);
});
export const Keywords: string[] = [];
for (const name in keywords) {
if (!keywords[name].map) continue;
Keywords.push(name);
if (keywords[name].abbr) Keywords.push(...keywords[name].abbr!);
}
export const all = { Properties, Operators: [...Operators, 'of'], Keywords };

View File

@@ -1,14 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { Expression } from '../language/expression';
export type Transpiler = (source: string) => Expression
export const Transpiler = (source: string) => Expression;

View File

@@ -1,63 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import * as P from '../../mol-util/monadic-parser';
import { Expression } from '../language/expression';
export interface AtomGroupArgs {
[index: string]: any
'entity-test'?: Expression
'chain-test'?: Expression
'residue-test'?: Expression
'atom-test'?: Expression
'groupBy'?: Expression
}
export interface Keyword {
'@desc': string
abbr?: string[]
map?: () => Expression /* not given means the keyword is unsupported */
}
export type KeywordDict = { [name: string]: Keyword }
export interface Property {
'@desc': string
'@examples': string[]
isUnsupported?: boolean
isNumeric?: boolean
abbr?: string[]
regex: RegExp
map: (s: string) => any
level: 'atom-test' | 'residue-test' | 'chain-test' | 'entity-test'
property?: Expression
}
export type PropertyDict = { [name: string]: Property }
export interface Operator {
'@desc': string
'@examples': string[]
name: string
abbr?: string[]
isUnsupported?: boolean
type: (p1: P.MonadicParser<any>, p2: P.MonadicParser<any>, fn: any) => P.MonadicParser<any>
rule: P.MonadicParser<any>
map: (x: any, y: any, z?: any) => Expression | Expression[]
}
export type OperatorList = Operator[]
export interface Function {
'@desc': string
'@examples': string[]
map?: (x: any) => Expression /* not given means the keyword is unsupported */
}
export type FunctionDict = { [name: string]: Function }

View File

@@ -1,79 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
export const examples = [{
name: 'All water residues',
value: 'water'
}, {
name: 'All C-alpha atoms',
value: 'name CA'
}, {
name: 'Residue 35',
value: 'resid 35'
}, {
name: 'C-alpha atoms of ALA',
value: 'name CA and resname ALA'
}, {
name: 'Backbone atoms',
value: 'backbone'
}, {
name: 'Non-protein atoms',
value: 'not protein'
}, {
name: 'Protein backbone or hydrogen atoms',
value: 'protein (backbone or name H)'
}, {
name: 'Atoms heavier than 20',
value: 'mass > 20'
}, {
name: 'Atoms with two bonds',
value: 'numbonds = 2'
}, {
name: 'Atoms with an absolute charge greater 1',
value: 'abs(charge) > 1'
}, {
name: 'Atoms with an x coordinate between -25 and -20',
value: 'x < -20 and x > -25'
}, {
name: 'Helices',
value: 'structure H'
}, {
name: 'Atoms with name "A 1"',
value: "name 'A 1'"
}, {
name: 'Atoms with name "A *"',
value: "name 'A *'"
}, {
name: 'Atoms with names starting with C',
value: 'name "C.*"'
}, {
name: 'Atoms within 10 ang of [25, 15, 10]',
value: 'sqr(x+25)+sqr(y+15)+sqr(z+10) <= sqr(10)'
}, {
name: 'Atoms within 5 ang of iron atoms',
value: 'within 5 of name FE'
}, {
name: 'Atoms around 10 ang of HEM residue',
value: 'exwithin 10 of resname HEM'
}, {
name: 'ALA residues within 15 ang of HEM',
value: 'resname ALA within 15 of resname HEM'
}, {
name: 'All groups that include an iron atom',
value: 'same resid as name FE'
}, {
name: 'Atoms with mass between 12 and 17.5',
value: 'mass 12 to 17.5'
}, {
name: 'Residues 60, 80, 90 and 142',
value: 'resid 60 80 90 142'
}/* , {
name: 'Residues ala, arg, asn, asp, cys, and tyr',
value: 'resname ALA to CYS TYR'
}*/];

View File

@@ -1,100 +0,0 @@
/**
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { FunctionDict } from '../types';
export const functions: FunctionDict = {
'sqr': {
'@desc': 'square of x',
'@examples': ['sqr(2)'],
map: x => B.core.math.pow([x, 2]),
},
'sqrt': {
'@desc': 'square root of x',
'@examples': ['sqrt(2)'],
map: x => B.core.math.sqrt([x]),
},
'abs': {
'@desc': 'absolute value of x',
'@examples': ['abs(2)'],
map: x => B.core.math.abs([x]),
},
'floor': {
'@desc': 'largest integer not greater than x',
'@examples': ['floor(2)'],
map: x => B.core.math.floor([x]),
},
'ceil': {
'@desc': 'smallest integer not less than x',
'@examples': ['ceil(2)'],
map: x => B.core.math.ceil([x]),
},
'sin': {
'@desc': 'sine of x',
'@examples': ['sin(2)'],
map: x => B.core.math.sin([x]),
},
'cos': {
'@desc': 'cosine of x',
'@examples': ['cos(2)'],
map: x => B.core.math.cos([x]),
},
'tan': {
'@desc': 'tangent of x',
'@examples': ['tan(2)'],
map: x => B.core.math.tan([x]),
},
'atan': {
'@desc': 'arctangent of x',
'@examples': ['atan(2)'],
map: x => B.core.math.atan([x]),
},
'asin': {
'@desc': 'arcsin of x',
'@examples': ['asin(2)'],
map: x => B.core.math.asin([x]),
},
'acos': {
'@desc': 'arccos of x',
'@examples': ['acos(2)'],
map: x => B.core.math.acos([x]),
},
'sinh': {
'@desc': 'hyperbolic sine of x',
'@examples': ['sinh(2)'],
map: x => B.core.math.sinh([x]),
},
'cosh': {
'@desc': 'hyperbolic cosine of x',
'@examples': ['cosh(2)'],
map: x => B.core.math.cosh([x]),
},
'tanh': {
'@desc': 'hyperbolic tangent of x',
'@examples': ['tanh(2)'],
map: x => B.core.math.tanh([x]),
},
'exp': {
'@desc': 'e to the power x',
'@examples': ['exp(2)'],
map: x => B.core.math.exp([x]),
},
'log': {
'@desc': 'natural log of x',
'@examples': ['log(2)'],
map: x => B.core.math.log([x]),
},
'log10': {
'@desc': 'log base 10 of x',
'@examples': ['log10(2)'],
map: x => B.core.math.log10([x]),
}
};

View File

@@ -1,303 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import * as h from '../helper';
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { KeywordDict } from '../types';
function proteinExpr() {
return B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.set.isSubset([
h.atomNameSet(['C', 'N', 'CA', 'O']),
B.ammpSet('label_atom_id')
])
});
}
function nucleicExpr() {
return B.struct.filter.pick({
0: B.struct.generator.atomGroups({
'group-by': B.ammp('residueKey')
}),
test: B.core.logic.and([
B.core.set.isSubset([
h.atomNameSet(['P']),
B.ammpSet('label_atom_id')
]),
B.core.logic.or([
B.core.set.isSubset([
h.atomNameSet(["O3'", "C3'", "C4'", "C5'", "O5'"]),
B.ammpSet('label_atom_id')
]),
B.core.set.isSubset([
h.atomNameSet(['O3*', 'C3*', 'C4*', 'C5*', 'O5*']),
B.ammpSet('label_atom_id')
])
])
])
});
}
function backboneExpr() {
return B.struct.combinator.merge([
B.struct.generator.queryInSelection({
0: proteinExpr(),
query: B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
h.atomNameSet(Backbone.protein),
B.ammp('label_atom_id')
])
})
}),
B.struct.generator.queryInSelection({
0: nucleicExpr(),
query: B.struct.generator.atomGroups({
'atom-test': B.core.set.has([
h.atomNameSet(Backbone.nucleic),
B.ammp('label_atom_id')
])
})
})
]);
}
function secStrucExpr(flags: string[]) {
return B.struct.generator.atomGroups({
'residue-test': B.core.flags.hasAll([
B.ammp('secondaryStructureFlags'),
B.struct.type.secondaryStructureFlags(flags)
])
});
}
const Backbone = {
nucleic: ['P', "O3'", "O5'", "C5'", "C4'", "C3'", 'OP1', 'OP2', 'O3*', 'O5*', 'C5*', 'C4*', 'C3*'],
protein: ['C', 'N', 'CA', 'O']
};
const ResDict = {
acidic: ['ASP', 'GLU'],
aliphatic: ['ALA', 'GLY', 'ILE', 'LEU', 'VAL'],
aromatic: ['HIS', 'PHE', 'TRP', 'TYR'],
at: ['ADA', 'A', 'THY', 'T'],
basic: ['ARG', 'HIS', 'LYS'],
buried: ['ALA', 'LEU', 'VAL', 'ILE', 'PHE', 'CYS', 'MET', 'TRP'],
cg: ['CYT', 'C', 'GUA', 'G'],
cyclic: ['HIS', 'PHE', 'PRO', 'TRP', 'TYR'],
hydrophobic: ['ALA', 'LEU', 'VAL', 'ILE', 'PRO', 'PHE', 'MET', 'TRP'],
medium: ['VAL', 'THR', 'ASP', 'ASN', 'PRO', 'CYS', 'ASX', 'PCA', 'HYP'],
neutral: ['VAL', 'PHE', 'GLN', 'TYR', 'HIS', 'CYS', 'MET', 'TRP', 'ASX', 'GLX', 'PCA', 'HYP'],
purine: ['ADE', 'A', 'GUA', 'G'],
pyrimidine: ['CYT', 'C', 'THY', 'T', 'URI', 'U'],
small: ['ALA', 'GLY', 'SER'],
water: ['H2O', 'HH0', 'OHH', 'HOH', 'OH2', 'SOL', 'WAT', 'TIP', 'TIP2', 'TIP3', 'TIP4']
};
export const keywords: KeywordDict = {
all: {
'@desc': 'everything',
map: () => B.struct.generator.all()
},
none: {
'@desc': 'nothing',
map: () => B.struct.generator.empty()
},
protein: {
'@desc': 'a residue with atoms named C, N, CA, and O',
map: () => proteinExpr()
},
nucleic: {
'@desc': "a residue with atoms named P, O1P, O2P and either O3', C3', C4', C5', O5' or O3*, C3*, C4*, C5*, O5*. This definition assumes that the base is phosphorylated, an assumption which will be corrected in the future.",
map: () => nucleicExpr()
},
backbone: {
'@desc': 'the C, N, CA, and O atoms of a protein and the equivalent atoms in a nucleic acid.',
map: () => backboneExpr()
},
sidechain: {
'@desc': 'non-backbone atoms and bonds', // TODO: what does 'bonds' mean here?
map: () => h.invertExpr(backboneExpr())
},
water: {
'@desc': 'all atoms with the resname H2O, HH0, OHH, HOH, OH2, SOL, WAT, TIP, TIP2, TIP3 or TIP4',
abbr: ['waters'],
map: () => h.resnameExpr(ResDict.water)
},
at: {
'@desc': 'residues named ADA A THY T',
map: () => h.resnameExpr(ResDict.at)
},
acidic: {
'@desc': 'residues named ASP GLU',
map: () => h.resnameExpr(ResDict.acidic)
},
acyclic: {
'@desc': '"protein and not cyclic"',
map: () => B.struct.modifier.intersectBy({
0: proteinExpr(),
by: h.invertExpr(h.resnameExpr(ResDict.cyclic))
})
},
aliphatic: {
'@desc': 'residues named ALA GLY ILE LEU VAL',
map: () => h.resnameExpr(ResDict.aliphatic)
},
alpha: {
'@desc': "atom's residue is an alpha helix",
map: () => secStrucExpr(['alpha'])
},
amino: {
'@desc': 'a residue with atoms named C, N, CA, and O',
map: () => proteinExpr()
},
aromatic: {
'@desc': 'residues named HIS PHE TRP TYR',
map: () => h.resnameExpr(ResDict.aromatic)
},
basic: {
'@desc': 'residues named ARG HIS LYS',
map: () => h.resnameExpr(ResDict.basic)
},
bonded: {
'@desc': 'atoms for which numbonds > 0',
map: () => h.asAtoms(B.struct.filter.pick({
'0': B.struct.modifier.includeConnected({
'0': B.struct.generator.all(),
'bond-test': B.core.flags.hasAny([
B.struct.bondProperty.flags(),
B.struct.type.bondFlags(['covalent', 'metallic', 'sulfide'])
])
}),
test: B.core.rel.gr([
B.struct.atomSet.atomCount(), 1
])
}))
},
buried: {
'@desc': 'residues named ALA LEU VAL ILE PHE CYS MET TRP',
map: () => h.resnameExpr(ResDict.buried)
},
cg: {
'@desc': 'residues named CYT C GUA G',
map: () => h.resnameExpr(ResDict.cg)
},
charged: {
'@desc': '"basic or acidic"',
map: () => h.resnameExpr(ResDict.basic.concat(ResDict.acidic))
},
cyclic: {
'@desc': 'residues named HIS PHE PRO TRP TYR',
map: () => h.resnameExpr(ResDict.cyclic)
},
hetero: {
'@desc': '"not (protein or nucleic)"',
map: () => h.invertExpr(
B.struct.combinator.merge([proteinExpr(), nucleicExpr()])
)
},
hydrogen: {
'@desc': 'name "[0-9]?H.*"',
map: () => B.struct.generator.atomGroups({
'atom-test': B.core.str.match([
B.core.type.regex(['^[0-9]?[H].*$', 'i']),
B.core.type.str([B.ammp('label_atom_id')])
])
})
},
large: {
'@desc': '"protein and not (small or medium)"',
map: () => B.struct.modifier.intersectBy({
0: proteinExpr(),
by: h.invertExpr(
h.resnameExpr(ResDict.small.concat(ResDict.medium))
)
})
},
medium: {
'@desc': 'residues named VAL THR ASP ASN PRO CYS ASX PCA HYP',
map: () => h.resnameExpr(ResDict.medium)
},
neutral: {
'@desc': 'residues named VAL PHE GLN TYR HIS CYS MET TRP ASX GLX PCA HYP',
map: () => h.resnameExpr(ResDict.neutral)
},
hydrophobic: {
'@desc': 'hydrophobic resname ALA LEU VAL ILE PRO PHE MET TRP',
map: () => h.resnameExpr(ResDict.hydrophobic)
},
polar: {
'@desc': '"protein and not hydrophobic"',
map: () => B.struct.modifier.intersectBy({
0: proteinExpr(),
by: h.invertExpr(h.resnameExpr(ResDict.hydrophobic))
})
},
purine: {
'@desc': 'residues named ADE A GUA G',
map: () => h.resnameExpr(ResDict.purine)
},
pyrimidine: {
'@desc': 'residues named CYT C THY T URI U',
map: () => h.resnameExpr(ResDict.pyrimidine)
},
small: {
'@desc': 'residues named ALA GLY SER',
map: () => h.resnameExpr(ResDict.small)
},
surface: {
'@desc': '"protein and not buried"',
map: () => B.struct.modifier.intersectBy({
0: proteinExpr(),
by: h.invertExpr(h.resnameExpr(ResDict.buried))
})
},
alpha_helix: {
'@desc': "atom's residue is in an alpha helix",
map: () => secStrucExpr(['alpha'])
},
pi_helix: {
'@desc': "atom's residue is in a pi helix",
map: () => secStrucExpr(['pi'])
},
helix_3_10: {
'@desc': "atom's residue is in a 3-10 helix",
map: () => secStrucExpr(['3-10'])
},
helix: {
'@desc': "atom's residue is in an alpha or pi or 3-10 helix",
map: () => secStrucExpr(['helix'])
},
extended_beta: {
'@desc': "atom's residue is a beta sheet",
map: () => secStrucExpr(['sheet'])
},
bridge_beta: {
'@desc': "atom's residue is a beta sheet",
map: () => secStrucExpr(['strand'])
},
sheet: {
'@desc': "atom's residue is a beta sheet",
map: () => secStrucExpr(['beta'])
},
turn: {
'@desc': "atom's residue is in a turn conformation",
map: () => secStrucExpr(['turn'])
},
coil: {
'@desc': "atom's residue is in a coil conformation",
map: () => B.struct.modifier.intersectBy({
0: proteinExpr(),
by: secStrucExpr(['none'])
})
}
};

View File

@@ -1,77 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
import { functions } from './functions';
const _docs: string[] = [
'VMD',
'============',
'--------------------------------',
''
];
_docs.push(`## Properties\n\n`);
_docs.push('--------------------------------\n');
for (const name in properties) {
if (properties[name].isUnsupported) continue;
const names = [name];
if (properties[name].abbr) names.push(...properties[name].abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (properties[name]['@desc']) {
_docs.push(`*${properties[name]['@desc']}*\n`);
}
}
_docs.push(`## Operators\n\n`);
_docs.push('--------------------------------\n');
operators.forEach(o => {
if (o.isUnsupported) return;
const names = [o.name];
if (o.abbr) names.push(...o.abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (o['@desc']) {
_docs.push(`*${o['@desc']}*\n`);
}
});
_docs.push(`## Keywords\n\n`);
_docs.push('--------------------------------\n');
for (const name in keywords) {
if (!keywords[name].map) continue;
const names = [name];
if (keywords[name].abbr) names.push(...keywords[name].abbr!);
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (keywords[name]['@desc']) {
_docs.push(`*${keywords[name]['@desc']}*\n`);
}
}
_docs.push(`## Functions\n\n`);
_docs.push('--------------------------------\n');
for (const name in functions) {
if (!functions[name].map) continue;
const names = [name];
_docs.push(`\`\`\`\n${names.join(', ')}\n\`\`\`\n`);
if (functions[name]['@desc']) {
_docs.push(`*${functions[name]['@desc']}*\n`);
}
}
export const docs = _docs.join('\n');

View File

@@ -1,83 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import * as P from '../../../mol-util/monadic-parser';
import * as h from '../helper';
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { properties } from './properties';
import { Expression } from '../../language/expression';
import { OperatorList } from '../types';
const propNames = Object.keys(properties).sort(h.strLenSortFn)
.filter(name => !properties[name].isUnsupported).join('|');
export const operators: OperatorList = [
{
'@desc': 'Selects atoms that are not included in s1.',
'@examples': ['not protein'],
name: 'not',
type: h.prefix,
rule: P.MonadicParser.regexp(/NOT/i).skip(P.MonadicParser.whitespace),
map: (op, selection) => h.invertExpr(selection),
},
{
'@desc': 'Selects atoms within a specified distance of a selection',
'@examples': ['within 5 of name FE'],
name: 'within',
type: h.prefix,
rule: h.prefixOp(/WITHIN\s+([-+]?[0-9]*\.?[0-9]+)\s+OF/i, 1).map((x: any) => parseFloat(x)),
map: (radius: number, selection: Expression) => {
return B.struct.modifier.includeSurroundings({ 0: selection, radius });
}
},
{
'@desc': 'Exclusive within, equivalent to (within 3 of X) and not X',
'@examples': ['exwithin 10 of resname HEM'],
name: 'exwithin',
type: h.prefix,
rule: h.prefixOp(/EXWITHIN\s+([-+]?[0-9]*\.?[0-9]+)\s+OF/i, 1).map((x: any) => parseFloat(x)),
map: (radius: number, target: Expression) => {
return B.struct.modifier.exceptBy({
'0': B.struct.modifier.includeSurroundings({ 0: target, radius }),
by: target
});
}
},
{
'@desc': 'Selects atoms which have the same keyword as the atoms in a given selection',
'@examples': ['same resid as name FE'],
name: 'same',
type: h.prefix,
rule: h.prefixOp(new RegExp(`SAME\\s+(${propNames})\\s+AS`, 'i'), 1).map((x: any) => properties[x].property),
map: (property: Expression, source: Expression) => {
return B.struct.filter.withSameAtomProperties({
'0': B.struct.generator.all(),
source,
property
});
}
},
{
'@desc': 'Selects atoms included in both s1 and s2.',
'@examples': ['backbone and protein'],
name: 'and',
type: h.binaryLeft,
rule: P.MonadicParser.alt(h.infixOp(/AND/i), P.MonadicParser.whitespace),
map: (op, selection, by) => B.struct.modifier.intersectBy({ 0: selection, by })
},
{
'@desc': 'Selects atoms included in either s1 or s2.',
'@examples': ['water or protein'],
name: 'or',
type: h.binaryLeft,
rule: h.infixOp(/OR/i),
map: (op, s1, s2) => B.struct.combinator.merge([s1, s2])
}
];

View File

@@ -1,262 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
* @author Koya Sakuma <koya.sakuma.work@gmail.com>
*
* Adapted from MolQL project
*/
import * as P from '../../../mol-util/monadic-parser';
import * as h from '../helper';
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { sstrucMap, sstrucDict, properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
import { functions } from './functions';
import { OperatorList } from '../types';
import { Transpiler } from '../transpiler';
// <, <=, = or ==, >=, >, and !=
// lt, le, eq, ge, gt, and ne, =~
const valueOperators: OperatorList = [
{
'@desc': 'multiplication, division',
'@examples': [],
name: 'mul-div',
type: h.binaryLeft,
rule: P.MonadicParser.regexp(/\s*(\*|\/)\s*/, 1),
map: (op, e1, e2) => {
switch (op) {
case '*': return B.core.math.mult([e1, e2]);
case '/': return B.core.math.div([e1, e2]);
default: throw new Error(`value operator '${op}' not supported`);
}
}
},
{
'@desc': 'addition, substraction',
'@examples': [],
name: 'add-sub',
type: h.binaryLeft,
rule: P.MonadicParser.regexp(/\s*(-|\+)\s*/, 1),
map: (op, e1, e2) => {
switch (op) {
case '-': return B.core.math.sub([e1, e2]);
case '+': return B.core.math.add([e1, e2]);
default: throw new Error(`value operator '${op}' not supported`);
}
}
},
{
'@desc': 'value comparisons',
'@examples': [],
name: 'comparison',
type: h.binaryLeft,
rule: P.MonadicParser.alt(P.MonadicParser.regexp(/\s*(=~|==|>=|<=|=|!=|>|<)\s*/, 1), P.MonadicParser.whitespace.result('=')),
map: (op, e1, e2) => {
let expr;
if (e1.head !== undefined) {
if (e1.head.name === 'structure-query.atom-property.macromolecular.secondary-structure-flags') {
expr = B.core.flags.hasAny([e1, sstrucMap(e2)]);
}
if (e1.head.name === 'core.type.regex') {
expr = B.core.str.match([e1, B.core.type.str([e2])]);
}
} else if (e2.head !== undefined) {
if (e2.head.name === 'structure-query.atom-property.macromolecular.secondary-structure-flags') {
expr = B.core.flags.hasAny([e2, sstrucMap(e1)]);
}
if (e2.head.name === 'core.type.regex') {
expr = B.core.str.match([e2, B.core.type.str([e1])]);
}
} else if (op === '=~') {
if (e1.head) {
expr = B.core.str.match([
B.core.type.regex([`^${e2}$`, 'i']),
B.core.type.str([e1])
]);
} else {
expr = B.core.str.match([
B.core.type.regex([`^${e1}$`, 'i']),
B.core.type.str([e2])
]);
}
}
if (!expr) {
if (e1.head) e2 = h.wrapValue(e1, e2);
if (e2.head) e1 = h.wrapValue(e2, e1);
switch (op) {
case '=':
case '==':
expr = B.core.rel.eq([e1, e2]);
break;
case '!=':
expr = B.core.rel.neq([e1, e2]);
break;
case '>':
expr = B.core.rel.gr([e1, e2]);
break;
case '<':
expr = B.core.rel.lt([e1, e2]);
break;
case '>=':
expr = B.core.rel.gre([e1, e2]);
break;
case '<=':
expr = B.core.rel.lte([e1, e2]);
break;
default: throw new Error(`value operator '${op}' not supported`);
}
}
return B.struct.generator.atomGroups({ 'atom-test': expr });
}
}
];
const lang = P.MonadicParser.createLanguage({
Parens: function (r: any) {
return P.MonadicParser.alt(
r.Parens,
r.Operator,
r.Expression
).wrap(P.MonadicParser.string('('), P.MonadicParser.string(')'));
},
Expression: function (r: any) {
return P.MonadicParser.alt(
r.RangeListProperty,
// r.NamedAtomProperties,
r.ValueQuery,
r.Keywords,
);
},
NamedAtomProperties: function () {
return P.MonadicParser.alt(...h.getNamedPropertyRules(properties));
},
Keywords: () => P.MonadicParser.alt(...h.getKeywordRules(keywords)),
ValueRange: function (r: any) {
return P.MonadicParser.seq(
r.Value
.skip(P.MonadicParser.regexp(/\s+TO\s+/i)),
r.Value
).map(x => ({ range: x }));
},
RangeListProperty: function (r: any) {
return P.MonadicParser.seq(
P.MonadicParser.alt(...h.getPropertyNameRules(properties, /\s/))
.skip(P.MonadicParser.whitespace),
P.MonadicParser.alt(
r.ValueRange,
r.Value
).sepBy1(P.MonadicParser.whitespace)
).map(x => {
const [property, values] = x;
const listValues: (string | number)[] = [];
const rangeValues: any[] = [];
values.forEach((v: any) => {
if (v.range) {
rangeValues.push(
B.core.rel.inRange([property, v.range[0], v.range[1]])
);
} else {
listValues.push(h.wrapValue(property, v, sstrucDict));
}
});
const rangeTest = h.orExpr(rangeValues);
const listTest = h.valuesTest(property, listValues);
let test;
if (rangeTest && listTest) {
test = B.core.logic.or([rangeTest, listTest]);
} else {
test = rangeTest ? rangeTest : listTest;
}
return B.struct.generator.atomGroups({ [h.testLevel(property)]: test });
});
},
Operator: function (r: any) {
return h.combineOperators(operators, P.MonadicParser.alt(r.Parens, r.Expression, r.ValueQuery));
},
Query: function (r: any) {
return P.MonadicParser.alt(
r.Operator,
r.Parens,
r.Expression
).trim(P.MonadicParser.optWhitespace);
},
Number: function () {
return P.MonadicParser.regexp(/-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?/)
.map(Number)
.desc('number');
},
String: function () {
const w = h.getReservedWords(properties, keywords, operators)
.sort(h.strLenSortFn).map(h.escapeRegExp).join('|');
return P.MonadicParser.alt(
P.MonadicParser.regexp(new RegExp(`(?!(${w}))[A-Z0-9_]+`, 'i')),
P.MonadicParser.regexp(/'((?:[^"\\]|\\.)*)'/, 1),
P.MonadicParser.regexp(/"((?:[^"\\]|\\.)*)"/, 1).map((x: any) => B.core.type.regex([`^${x}$`, 'i']))
).desc('string');
},
Value: function (r: any) {
return P.MonadicParser.alt(r.Number, r.String);
},
ValueParens: function (r: any) {
return P.MonadicParser.alt(
r.ValueParens,
r.ValueOperator,
r.ValueExpressions
).wrap(P.MonadicParser.string('('), P.MonadicParser.string(')'));
},
ValuePropertyNames: function () {
return P.MonadicParser.alt(...h.getPropertyNameRules(properties, /=~|==|>=|<=|=|!=|>|<|\)|\s|\+|-|\*|\//i));
},
ValueOperator: function (r: any) {
return h.combineOperators(valueOperators, P.MonadicParser.alt(r.ValueParens, r.ValueExpressions));
},
ValueExpressions: function (r: any) {
return P.MonadicParser.alt(
r.ValueFunctions,
r.Value,
r.ValuePropertyNames
);
},
ValueFunctions: function (r: any) {
return P.MonadicParser.alt(...h.getFunctionRules(functions, r.ValueOperator));
},
ValueQuery: function (r: any) {
return P.MonadicParser.alt(
r.ValueOperator.map((x: any) => {
// if (!x.head || x.head.startsWith('core.math') || x.head.startsWith('structure-query.atom-property')) {
if (!x.head.name || !x.head.name.startsWith('structure-query.generator')) {
throw new Error(`values must be part of an comparison, value '${x}'`);
} else {
return x as any;
}
})
);
}
});
export const transpiler: Transpiler = str => lang.Query.tryParse(str);

View File

@@ -1,269 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { MolScriptBuilder } from '../../../mol-script/language/builder';
const B = MolScriptBuilder;
import { PropertyDict } from '../types';
const reFloat = /[-+]?[0-9]*\.?[0-9]+/;
const rePosInt = /[+]?[0-9]+/;
const reInt = /[-+]?[0-9]+/;
function str(x: string) { return x; }
export const sstrucDict: { [key: string]: string } = {
T: 'turn', // Turn
E: 'sheet', // Extended conformation ($\beta$ sheets)
B: 'strand', // Isolated bridge
H: 'alpha', // Alpha helix
G: '3-10', // 3-10 helix
I: 'pi', // Pi helix
C: 'none', // Coil
};
export function sstrucMap(x: string) {
return B.struct.type.secondaryStructureFlags(
[sstrucDict[x.toUpperCase()] || 'none']
);
}
export const properties: PropertyDict = {
name: {
'@desc': 'str atom name',
'@examples': ['name CA'],
regex: /[a-zA-Z0-9]+/, map: B.atomName,
level: 'atom-test', property: B.ammp('label_atom_id')
},
type: {
'@desc': 'str atom type',
'@examples': ['type C3'],
isUnsupported: true,
regex: /[a-zA-Z0-9]+/, map: str,
level: 'atom-test'
},
index: {
'@desc': 'num the atom number, starting at 0',
'@examples': ['index 10'],
isNumeric: true,
regex: rePosInt, map: x => (parseInt(x) - 1),
level: 'atom-test', property: B.ammp('id')
},
serial: {
'@desc': 'num the atom number, starting at 1',
'@examples': ['serial 11'],
isNumeric: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test', property: B.ammp('id')
},
atomicnumber: {
'@desc': 'num atomic number (0 if undefined)',
'@examples': ['atomicnumber 13'],
isNumeric: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test', property: B.acp('atomicNumber')
},
element: {
'@desc': 'str atomic element symbol string ("X" if undefined)',
'@examples': ['element N'],
regex: /[a-zA-Z0-9]{1,3}/, map: x => B.es(x),
level: 'atom-test', property: B.acp('elementSymbol')
},
altloc: {
'@desc': 'str alternate location/conformation identifier',
'@examples': ['altloc C'],
regex: /[a-zA-Z0-9]+/, map: str,
level: 'atom-test', property: B.ammp('label_alt_id')
},
chain: {
'@desc': 'str the one-character chain identifier',
'@examples': ['chain A'],
regex: /[a-zA-Z0-9]+/, map: str,
level: 'residue-test', property: B.ammp('auth_asym_id')
},
residue: {
'@desc': 'num a set of connected atoms with the same residue number',
'@examples': ['residue < 11', 'residue 11'],
isNumeric: true,
regex: reInt, map: x => parseInt(x),
level: 'residue-test', property: B.ammp('auth_seq_id')
},
fragment: {
'@desc': 'num a set of connected residues',
'@examples': ['fragment 42'],
isUnsupported: true,
isNumeric: true,
regex: reInt, map: x => parseInt(x),
level: 'residue-test'
},
pfrag: {
'@desc': 'num a set of connected protein residues',
'@examples': ['pfrag 42'],
isUnsupported: true,
isNumeric: true,
regex: reInt, map: x => parseInt(x),
level: 'residue-test'
},
nfrag: {
'@desc': 'num a set of connected nucleic residues',
'@examples': ['nfrag 42'],
isUnsupported: true,
isNumeric: true,
regex: reInt, map: x => parseInt(x),
level: 'residue-test'
},
sequence: {
'@desc': 'str a sequence given by one letter names',
'@examples': ['sequence PGATTACA'],
isUnsupported: true,
regex: /[a-zA-Z0-9]+/, map: str,
level: 'residue-test'
},
numbonds: {
'@desc': 'num number of bonds',
'@examples': ['numbonds = 2', 'numbonds >= 3'],
isNumeric: true,
regex: rePosInt, map: x => parseInt(x),
level: 'atom-test', property: B.acp('bondCount')
},
resname: {
'@desc': 'str residue name',
'@examples': ['resname ALA'],
regex: /[a-zA-Z0-9]+/, map: str,
level: 'residue-test', property: B.ammp('auth_comp_id')
},
resid: {
'@desc': 'num residue id',
'@examples': ['resid 42'],
isNumeric: true,
regex: reInt, map: x => parseInt(x),
level: 'residue-test', property: B.ammp('auth_seq_id')
},
segname: {
'@desc': 'str segment name',
'@examples': ['segname B'],
regex: /[a-zA-Z0-9]+/, map: str,
level: 'residue-test', property: B.ammp('label_asym_id')
},
x: {
'@desc': 'float x coordinate',
'@examples': ['x 42'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('x')
},
y: {
'@desc': 'float y coordinate',
'@examples': ['y > 1.7'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('y')
},
z: {
'@desc': 'float z coordinate',
'@examples': ['z < 11', 'z > -21'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('z')
},
radius: {
'@desc': 'float atomic radius',
'@examples': ['radius > 1.3'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('vdw')
},
mass: {
'@desc': 'float atomic mass',
'@examples': ['mass > 2'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.acp('mass')
},
charge: {
'@desc': 'float atomic charge',
'@examples': ['charge > 0', 'charge 1'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('pdbx_formal_charge')
},
beta: {
'@desc': 'float temperature factor',
'@examples': ['beta < 20', 'beta > 35'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('B_iso_or_equiv')
},
occupancy: {
'@desc': 'float occupancy',
'@examples': ['occupancy 1', 'occupancy < 1'],
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test', property: B.ammp('occupancy')
},
user: {
'@desc': 'float time-varying user-specified value',
'@examples': ['user < 0.1'],
isUnsupported: true,
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'atom-test'
},
rasmol: {
'@desc': 'str translates Rasmol selection string to VMD',
'@examples': ["rasmol 'all'"],
isUnsupported: true,
regex: /[^']*/, map: str,
level: 'atom-test'
},
structure: {
'@desc': 'str single letter name for the secondary structure',
'@examples': ['structure H', 'structure H E'],
regex: /T|E|B|H|G|I|C/i, map: sstrucMap,
level: 'atom-test', property: B.ammp('secondaryStructureFlags')
},
phi: {
'@desc': 'float phi backbone conformational angles',
'@examples': ['phi < 160'],
isUnsupported: true,
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'residue-test'
},
psi: {
'@desc': 'float psi backbone conformational angles',
'@examples': ['psi < 160'],
isUnsupported: true,
isNumeric: true,
regex: reFloat, map: x => parseFloat(x),
level: 'residue-test'
},
ufx: {
'@desc': 'num force to apply in the x coordinate',
'@examples': ['ufx 1'],
isUnsupported: true,
isNumeric: true,
regex: reFloat, map: x => parseInt(x),
level: 'atom-test'
},
ufy: {
'@desc': 'num force to apply in the y coordinate',
'@examples': ['ufy 1'],
isUnsupported: true,
isNumeric: true,
regex: reFloat, map: x => parseInt(x),
level: 'atom-test'
},
ufz: {
'@desc': 'num force to apply in the z coordinate',
'@examples': ['ufz 1'],
isUnsupported: true,
isNumeric: true,
regex: reFloat, map: x => parseInt(x),
level: 'atom-test'
},
};

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
*
* Adapted from MolQL project
*/
import { properties } from './properties';
import { operators } from './operators';
import { keywords } from './keywords';
import { functions } from './functions';
export const Properties: string[] = [];
for (const name in properties) {
if (properties[name].isUnsupported) continue;
Properties.push(name);
if (properties[name].abbr) Properties.push(...properties[name].abbr!);
}
export const Operators: string[] = [];
operators.forEach(o => {
if (o.isUnsupported) return;
Operators.push(o.name);
if (o.abbr) Operators.push(...o.abbr);
});
export const Keywords: string[] = [];
for (const name in keywords) {
if (!keywords[name].map) continue;
Keywords.push(name);
if (keywords[name].abbr) Keywords.push(...keywords[name].abbr!);
}
export const Functions: string[] = [];
for (const name in functions) {
if (!functions[name].map) continue;
Functions.push(name);
}
export const all = { Properties, Operators: [...Operators, ...Functions], Keywords };

View File

@@ -84,7 +84,7 @@ namespace StateAction {
}
export namespace Builder {
export interface Type<A extends StateObject.Ctor, P extends {}> {
export interface Type<A extends StateObject.Ctor, P extends { }> {
from?: A | A[],
params?: PD.For<P> | ((a: StateObject.From<A>, globalCtx: any) => PD.For<P>),
display?: string | { name: string, description?: string },
@@ -95,7 +95,7 @@ namespace StateAction {
<A extends StateObject.Ctor, P extends { }>(info: Type<A, P>): Define<StateObject.From<A>, PD.Normalize<P>>
}
export interface Define<A extends StateObject, P extends {}> {
export interface Define<A extends StateObject, P> {
<T>(def: DefinitionBase<A, T, P> | DefinitionBase<A, T, P>['run']): StateAction<A, T, P>,
}

View File

@@ -196,7 +196,7 @@ namespace Transformer {
}
export namespace Builder {
export interface Type<A extends StateObject.Ctor, B extends StateObject.Ctor, P extends {}> {
export interface Type<A extends StateObject.Ctor, B extends StateObject.Ctor, P extends { }> {
name: string,
from: A | A[],
to: B | B[],
@@ -210,7 +210,7 @@ namespace Transformer {
<A extends StateObject.Ctor, B extends StateObject.Ctor, P extends { }>(info: Type<A, B, P>): Define<StateObject.From<A>, StateObject.From<B>, PD.Normalize<P>>
}
export interface Define<A extends StateObject, B extends StateObject, P extends {}> {
export interface Define<A extends StateObject, B extends StateObject, P> {
(def: DefinitionBase<A, B, P>): Transformer<A, B, P>
}

View File

@@ -24,10 +24,6 @@ namespace Binding {
return { triggers, action, description };
}
export function isBinding(x: any): x is Binding {
return !!x && Array.isArray(x.triggers) && typeof x.action === 'string';
}
export const Empty: Binding = { triggers: [], action: '', description: '' };
export function isEmpty(binding: Binding) {
return binding.triggers.length === 0 ||

View File

@@ -117,7 +117,7 @@ export function defaults<T>(value: T | undefined, defaultValue: T): T {
return value !== undefined ? value : defaultValue;
}
export function extend<S extends {}, T extends {}, U extends {}>(object: S, source: T, guard?: U): S & T & U {
export function extend<S, T, U>(object: S, source: T, guard?: U): S & T & U {
let v: any;
const s = <any>source;
@@ -139,7 +139,7 @@ export function extend<S extends {}, T extends {}, U extends {}>(object: S, sour
return <any>object;
}
export function shallowClone<T extends {}>(o: T): T {
export function shallowClone<T>(o: T): T {
return extend({}, o) as T;
}
@@ -158,7 +158,7 @@ function _assign<T>(target: T): T {
export declare function _assignType<T>(o: T, ...from: any[]): T;
export const assign: (<T>(o: T, ...from: any[]) => T) = (Object as any).assign || _assign;
function _shallowMerge1<T extends {}>(source: T, update: T) {
function _shallowMerge1<T>(source: T, update: T) {
let changed = false;
for (const k of Object.keys(update)) {
if (!hasOwnProperty.call(update, k)) continue;

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