Compare commits

...

23 Commits

Author SHA1 Message Date
Alexander Rose
6edab203c2 0.7.0-dev.9 2020-04-24 18:49:20 -07:00
Alexander Rose
0abfdb5ee3 material icon css tweaks 2020-04-24 18:48:17 -07:00
Alexander Rose
88369158c9 0.7.0-dev.8 2020-04-24 18:01:32 -07:00
Alexander Rose
8926575283 larger volume-cell bounding-sphere radius 2020-04-24 18:00:00 -07:00
Alexander Rose
15b0288ce4 selection ui tooltip tweaks 2020-04-24 17:59:39 -07:00
Alexander Rose
28853ec19d 0.7.0-dev.7 2020-04-24 17:30:18 -07:00
Alexander Rose
e3480a076a tooltip tweaks 2020-04-24 16:28:32 -07:00
Alexander Rose
abf6452124 package updates 2020-04-24 16:20:05 -07:00
Alexander Rose
2cdd811dd3 repr preset tweaks- higher opacity since not double-side by default anymore- add snfg3d symbols for auto all-atom preset 2020-04-24 16:12:36 -07:00
Alexander Rose
be6fea39bf fog and image shader tweaks 2020-04-24 15:58:08 -07:00
Alexander Rose
ed1bc2cd07 don't set doubleSided=true when alpha<1 in getQualityProps 2020-04-24 15:57:41 -07:00
Alexander Rose
28d3d5861a add missing , BaseGeometry.CustomQualityParamInfo 2020-04-24 15:56:46 -07:00
Alexander Rose
95d3ef491f structure selection query improvements
- moved out of selection.tsx
- added queries for entities (based on entity description)
2020-04-24 13:21:30 -07:00
Alexander Rose
5c37ddfc6d add names for all elements 2020-04-24 13:20:00 -07:00
Alexander Rose
4d9e2d9c91 support title in ControlGroup and ActionMenu 2020-04-24 13:19:41 -07:00
Alexander Rose
0b1c18913d added MolScript.core.list.equal and MolScript.structureQuery.atomProperty.macromolecular.entityDescription 2020-04-24 12:35:08 -07:00
Alexander Rose
c8c2355d3e 0.7.0-dev.6 2020-04-23 20:43:57 -07:00
Alexander Rose
3d1366024d added more structure selection queries
- whole residues
- non-standard residues from current structures
- elements from current structures
2020-04-23 20:43:00 -07:00
Alexander Rose
f6964d2a66 added Structure.uniqueElementSymbols 2020-04-23 20:42:15 -07:00
Alexander Rose
de60f70af5 relax isApplicable validation-report checks 2020-04-23 16:33:41 -07:00
Alexander Rose
8f2e619162 fix assembly symmetry cage alignement 2020-04-23 16:32:44 -07:00
Alexander Rose
fbcef01c55 0.7.0-dev.5 2020-04-23 14:40:43 -07:00
Alexander Rose
641e0639d4 fix filehandle usage in server/ 2020-04-23 14:39:32 -07:00
31 changed files with 2041 additions and 989 deletions

2637
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -225,6 +225,7 @@ function getSymbolScale(symbol: string) {
function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.RotationAxes, size: number, structure: Structure) {
const eye = Vec3();
const target = Vec3();
const dir = Vec3();
const up = Vec3();
let pair: Mutable<AssemblySymmetry.RotationAxes> | undefined = undefined;
@@ -246,8 +247,8 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
const a5dir = Vec3.sub(Vec3(), a5.end, a5.start);
pair = [a5];
for (const a of axes.filter(a => a.order === 3)) {
let d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir));
if (equalEps(d, 100.81, 0.1) || equalEps(d, 79.19, 0.1)) {
const d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir));
if (!pair[1] && (equalEps(d, 100.81, 0.1) || equalEps(d, 79.19, 0.1))) {
pair[1] = a;
break;
}
@@ -263,8 +264,8 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
Vec3.copy(target, aA.end);
if (aB) {
Vec3.sub(up, aB.end, aB.start);
const d = Vec3.dot(eye, up);
if (d < 0) Vec3.negate(up, up);
Vec3.sub(dir, eye, target);
if (Vec3.dot(dir, up) < 0) Vec3.negate(up, up);
Mat4.targetTo(t, eye, target, up);
Mat4.scaleUniformly(t, t, size * getSymbolScale(symbol));
} else {

View File

@@ -340,7 +340,7 @@ export const ValidationReportDensityFitPreset = StructureRepresentationPresetPro
description: 'Color structure based on density fit. Data from wwPDB Validation Report, obtained via RCSB PDB.'
},
isApplicable(a) {
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.hasXrayMap(a.data.models[0]);
return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.isFromXray(a.data.models[0]) && Model.probablyHasDensityMap(a.data.models[0]);
},
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {

View File

@@ -67,7 +67,7 @@ export const DensityFitColorThemeProvider: ColorTheme.Provider<{}, ValidationRep
factory: DensityFitColorTheme,
getParams: () => ({}),
defaultValues: PD.getDefaultValues({}),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.hasXrayMap(ctx.structure.models[0]),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ValidationReport.isApplicable(ctx.structure.models[0]) && Model.isFromXray(ctx.structure.models[0]) && Model.probablyHasDensityMap(ctx.structure.models[0]),
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? ValidationReportProvider.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (data) => data.structure && data.structure.models[0].customProperties.reference(ValidationReportProvider.descriptor, false)

View File

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

View File

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

View File

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

View File

@@ -29,6 +29,7 @@ import { CustomProperties } from '../common/custom-property';
import { AtomicHierarchy } from '../model/properties/atomic';
import { StructureSelection } from '../query/selection';
import { getBoundary } from '../../../mol-math/geometry/boundary';
import { ElementSymbol } from '../model/types';
class Structure {
/** Maps unit.id to unit */
@@ -50,6 +51,7 @@ class Structure {
masterModel?: Model,
representativeModel?: Model,
uniqueResidueNames?: Set<string>,
uniqueElementSymbols?: Set<ElementSymbol>,
entityIndices?: ReadonlyArray<EntityIndex>,
uniqueAtomicResidueIndices?: ReadonlyMap<UUID, ReadonlyArray<ResidueIndex>>,
serialMapping?: SerialMapping,
@@ -265,6 +267,11 @@ class Structure {
|| (this._props.uniqueResidueNames = getUniqueResidueNames(this));
}
get uniqueElementSymbols() {
return this._props.uniqueElementSymbols
|| (this._props.uniqueElementSymbols = getUniqueElementSymbols(this));
}
get entityIndices() {
return this._props.entityIndices
|| (this._props.entityIndices = getEntityIndices(this));
@@ -403,7 +410,8 @@ function getUniqueResidueNames(s: Structure) {
const prop = StructureProperties.residue.label_comp_id;
const names = new Set<string>();
const loc = StructureElement.Location.create(s);
for (const unit of s.units) {
for (const unitGroup of s.unitSymmetryGroups) {
const unit = unitGroup.units[0];
// TODO: support coarse unit?
if (!Unit.isAtomic(unit)) continue;
const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
@@ -417,6 +425,22 @@ function getUniqueResidueNames(s: Structure) {
return names;
}
function getUniqueElementSymbols(s: Structure) {
const prop = StructureProperties.atom.type_symbol;
const symbols = new Set<ElementSymbol>();
const loc = StructureElement.Location.create(s);
for (const unitGroup of s.unitSymmetryGroups) {
const unit = unitGroup.units[0];
if (!Unit.isAtomic(unit)) continue;
loc.unit = unit;
for (let i = 0, il = unit.elements.length; i < il; ++i) {
loc.element = unit.elements[i];
symbols.add(prop(loc));
}
}
return symbols;
}
function getEntityIndices(structure: Structure): ReadonlyArray<EntityIndex> {
const { units } = structure;
const l = StructureElement.Location.create(structure);

View File

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

View File

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

View File

@@ -6,8 +6,8 @@
*/
import { CustomProperty } from '../../mol-model-props/common/custom-property';
import { QueryContext, Structure, StructureQuery, StructureSelection } from '../../mol-model/structure';
import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType } from '../../mol-model/structure/model/types';
import { QueryContext, Structure, StructureQuery, StructureSelection, StructureProperties, StructureElement } from '../../mol-model/structure';
import { BondType, NucleicBackboneAtoms, ProteinBackboneAtoms, SecondaryStructureType, AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames, ElementSymbol } from '../../mol-model/structure/model/types';
import { PluginContext } from '../../mol-plugin/context';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import Expression from '../../mol-script/language/expression';
@@ -15,9 +15,9 @@ import { compile } from '../../mol-script/runtime/query/compiler';
import { StateBuilder } from '../../mol-state';
import { RuntimeContext } from '../../mol-task';
import { SetUtils } from '../../mol-util/set';
import { stringToWords } from '../../mol-util/string';
import { PluginStateObject } from '../objects';
import { StateTransforms } from '../transforms';
import { ElementNames } from '../../mol-model/structure/model/properties/atomic/types';
export enum StructureSelectionCategory {
Type = 'Type',
@@ -41,6 +41,7 @@ interface StructureSelectionQuery {
readonly description: string
readonly category: string
readonly isHidden: boolean
readonly priority: number
readonly referencesCurrent: boolean
readonly query: StructureQuery
readonly ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
@@ -51,6 +52,7 @@ interface StructureSelectionQueryProps {
description?: string
category?: string
isHidden?: boolean
priority?: number
referencesCurrent?: boolean
ensureCustomProperties?: (ctx: CustomProperty.Context, structure: Structure) => Promise<void>
}
@@ -63,6 +65,7 @@ function StructureSelectionQuery(label: string, expression: Expression, props: S
description: props.description || '',
category: props.category ?? StructureSelectionCategory.Misc,
isHidden: !!props.isHidden,
priority: props.priority || 0,
referencesCurrent: !!props.referencesCurrent,
get query() {
if (!_query) _query = compile<StructureSelection>(expression);
@@ -81,7 +84,7 @@ function StructureSelectionQuery(label: string, expression: Expression, props: S
};
}
const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '' });
const all = StructureSelectionQuery('All', MS.struct.generator.all(), { category: '', priority: 1000 });
const current = StructureSelectionQuery('Current Selection', MS.internal.generator.current(), { category: '', referencesCurrent: true });
const polymer = StructureSelectionQuery('Polymer', MS.struct.modifier.union([
@@ -229,14 +232,14 @@ const branchedPlusConnected = StructureSelectionQuery('Carbohydrate with Connect
MS.struct.modifier.includeConnected({
0: branched.expression, 'layer-count': 1, 'as-whole-residues': true
})
]), { category: StructureSelectionCategory.Internal });
]), { category: StructureSelectionCategory.Internal, isHidden: true });
const branchedConnectedOnly = StructureSelectionQuery('Connected to Carbohydrate', MS.struct.modifier.union([
MS.struct.modifier.exceptBy({
0: branchedPlusConnected.expression,
by: branched.expression
})
]), { category: StructureSelectionCategory.Internal });
]), { category: StructureSelectionCategory.Internal, isHidden: true });
const ligand = StructureSelectionQuery('Ligand', MS.struct.modifier.union([
MS.struct.combinator.merge([
@@ -290,14 +293,14 @@ const ligandPlusConnected = StructureSelectionQuery('Ligand with Connected', MS.
]),
by: branched.expression
})
]), { category: StructureSelectionCategory.Internal });
]), { category: StructureSelectionCategory.Internal, isHidden: true });
const ligandConnectedOnly = StructureSelectionQuery('Connected to Ligand', MS.struct.modifier.union([
MS.struct.modifier.exceptBy({
0: ligandPlusConnected.expression,
by: ligand.expression
})
]), { category: StructureSelectionCategory.Internal });
]), { category: StructureSelectionCategory.Internal, isHidden: true });
// residues connected to ligands or branched entities
const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydrate', MS.struct.modifier.union([
@@ -305,7 +308,7 @@ const connectedOnly = StructureSelectionQuery('Connected to Ligand or Carbohydra
branchedConnectedOnly.expression,
ligandConnectedOnly.expression
]),
]), { category: StructureSelectionCategory.Internal });
]), { category: StructureSelectionCategory.Internal, isHidden: true });
const disulfideBridges = StructureSelectionQuery('Disulfide Bridges', MS.struct.modifier.union([
MS.struct.modifier.wholeResidues([
@@ -380,6 +383,16 @@ const bonded = StructureSelectionQuery('Residues Bonded to Selection', MS.struct
referencesCurrent: true
});
const wholeResidues = StructureSelectionQuery('Whole Residues of Selection', MS.struct.modifier.union([
MS.struct.modifier.wholeResidues({
0: MS.internal.generator.current()
})
]), {
description: 'Expand current selection to whole residues.',
category: StructureSelectionCategory.Manipulate,
referencesCurrent: true
});
const StandardAminoAcids = [
[['HIS'], 'HISTIDINE'],
[['ARG'], 'ARGININE'],
@@ -390,6 +403,7 @@ const StandardAminoAcids = [
[['TRP'], 'TRYPTOPHAN'],
[['ALA'], 'ALANINE'],
[['MET'], 'METHIONINE'],
[['PRO'], 'PROLINE'],
[['CYS'], 'CYSTEINE'],
[['ASN'], 'ASPARAGINE'],
[['VAL'], 'VALINE'],
@@ -402,6 +416,7 @@ const StandardAminoAcids = [
[['THR'], 'THREONINE'],
[['SEC'], 'SELENOCYSTEINE'],
[['PYL'], 'PYRROLYSINE'],
[['UNK'], 'UNKNOWN'],
].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
const StandardNucleicBases = [
@@ -411,14 +426,99 @@ const StandardNucleicBases = [
[['G', 'DG'], 'GUANOSINE'],
[['I', 'DI'], 'INOSINE'],
[['U', 'DU'], 'URIDINE'],
[['N', 'DN'], 'UNKNOWN'],
].sort((a, b) => a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0) as [string[], string][];
function ResidueQuery([names, label]: [string[], string], category: string) {
return StructureSelectionQuery(`${stringToWords(label)} (${names.join(', ')})`, MS.struct.modifier.union([
export function ResidueQuery([names, label]: [string[], string], category: string, priority = 0) {
return StructureSelectionQuery(`${label} (${names.join(', ')})`, MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'residue-test': MS.core.set.has([MS.set(...names), MS.ammp('auth_comp_id')])
})
]), { category });
]), { category, priority, description: label });
}
export function ElementSymbolQuery([names, label]: [string[], string], category: string, priority: number) {
return StructureSelectionQuery(`${label} (${names.join(', ')})`, MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'atom-test': MS.core.set.has([MS.set(...names), MS.acp('elementSymbol')])
})
]), { category, priority });
}
export function EntityDescriptionQuery([description, label]: [string[], string], category: string, priority: number) {
return StructureSelectionQuery(`${label}`, MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'entity-test': MS.core.list.equal([MS.list(...description), MS.ammp('entityDescription')])
})
]), { category, priority, description: description.join(', ') });
}
const StandardResidues = SetUtils.unionMany(
AminoAcidNamesL, RnaBaseNames, DnaBaseNames, WaterNames
);
export function getElementQueries(structures: Structure[]) {
const uniqueElements = new Set<ElementSymbol>();
for (const structure of structures) {
structure.uniqueElementSymbols.forEach(e => uniqueElements.add(e));
}
const queries: StructureSelectionQuery[] = [];
uniqueElements.forEach(e => {
const label = ElementNames[e] || e;
queries.push(ElementSymbolQuery([[e], label], 'Element Symbol', 0));
});
return queries;
}
export function getNonStandardResidueQueries(structures: Structure[]) {
const residueLabels = new Map<string, string>();
const uniqueResidues = new Set<string>();
for (const structure of structures) {
structure.uniqueResidueNames.forEach(r => uniqueResidues.add(r));
for (const m of structure.models) {
structure.uniqueResidueNames.forEach(r => {
const comp = m.properties.chemicalComponentMap.get(r);
if (comp) residueLabels.set(r, comp.name);
});
}
}
const queries: StructureSelectionQuery[] = [];
SetUtils.difference(uniqueResidues, StandardResidues).forEach(r => {
const label = residueLabels.get(r) || r;
queries.push(ResidueQuery([[r], label], 'Ligand/Non-standard Residue', 200));
});
return queries;
}
export function getPolymerAndBranchedEntityQueries(structures: Structure[]) {
const uniqueEntities = new Map<string, string[]>();
const l = StructureElement.Location.create();
for (const structure of structures) {
l.structure = structure;
for (const ug of structure.unitSymmetryGroups) {
l.unit = ug.units[0];
l.element = ug.elements[0];
const entityType = StructureProperties.entity.type(l);
if (entityType === 'polymer' || entityType === 'branched') {
const description = StructureProperties.entity.pdbx_description(l);
uniqueEntities.set(description.join(', '), description);
}
}
}
const queries: StructureSelectionQuery[] = [];
uniqueEntities.forEach((v, k) => {
queries.push(EntityDescriptionQuery([v, k], 'Polymer/Carbohydrate Entities', 300));
});
return queries;
}
export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
{ expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
{ tags: customTag ? [query, customTag] : [query] });
}
export const StructureSelectionQueries = {
@@ -447,14 +547,9 @@ export const StructureSelectionQueries = {
surroundings,
complement,
bonded,
wholeResidues,
};
export function applyBuiltInSelection(to: StateBuilder.To<PluginStateObject.Molecule.Structure>, query: keyof typeof StructureSelectionQueries, customTag?: string) {
return to.apply(StateTransforms.Model.StructureSelectionFromExpression,
{ expression: StructureSelectionQueries[query].expression, label: StructureSelectionQueries[query].label },
{ tags: customTag ? [query, customTag] : [query] });
}
export class StructureSelectionQueryRegistry {
list: StructureSelectionQuery[] = []
options: [StructureSelectionQuery, string, string][] = []

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ import Brush from '@material-ui/icons/Brush';
import Restore from '@material-ui/icons/Restore';
import Remove from '@material-ui/icons/Remove';
import * as React from 'react';
import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
import { StructureSelectionQueries, StructureSelectionQuery, getNonStandardResidueQueries, getElementQueries, getPolymerAndBranchedEntityQueries } from '../../mol-plugin-state/helpers/structure-selection-query';
import { InteractivityManager } from '../../mol-plugin-state/manager/interactivity';
import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component';
import { StructureRef, StructureComponentRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
@@ -25,6 +25,7 @@ import { Button, ControlGroup, IconButton, ToggleButton } from '../controls/comm
import { ParameterControls, ParamOnChange, PureSelectControl } from '../controls/parameters';
import { Union, Subtract, Intersect, SetSvg as SetSvg, CubeSvg } from '../controls/icons';
import { AddComponentControls } from './components';
import Structure from '../../mol-model/structure/structure/structure';
const StructureSelectionParams = {
granularity: InteractivityManager.Params.granularity,
@@ -60,6 +61,9 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
if (this.state.isEmpty !== isEmpty) {
this.setState({ isEmpty });
}
// trigger elementQueries and nonStandardResidueQueries recalculation
this.queriesVersion = -1;
this.forceUpdate();
});
this.subscribe(this.plugin.behaviors.state.isBusy, v => {
@@ -83,15 +87,28 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
this.plugin.managers.structure.selection.fromSelectionQuery(modifier, selectionQuery, false);
}
selectQuery: ActionMenu.OnSelect = item => {
selectQuery: ActionMenu.OnSelect = (item, e) => {
if (!item || !this.state.action) {
this.setState({ action: void 0 });
return;
}
const q = this.state.action! as StructureSelectionModifier;
this.setState({ action: void 0 }, () => {
if (e?.shiftKey) {
this.set(q, item.value as StructureSelectionQuery);
});
} else {
this.setState({ action: void 0 }, () => {
this.set(q, item.value as StructureSelectionQuery);
});
}
}
get structures () {
const structures: Structure[] = [];
for (const s of this.plugin.managers.structure.hierarchy.selection.structures) {
const structure = s.cell.obj?.data;
if (structure) structures.push(structure);
}
return structures;
}
private queriesItems: ActionMenu.Items[] = []
@@ -99,8 +116,15 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
get queries () {
const { registry } = this.plugin.query.structure;
if (registry.version !== this.queriesVersion) {
this.queriesItems = ActionMenu.createItems(registry.list, {
filter: q => q !== StructureSelectionQueries.current,
const structures = this.structures;
const queries = [
...registry.list,
...getPolymerAndBranchedEntityQueries(structures),
...getNonStandardResidueQueries(structures),
...getElementQueries(structures)
].sort((a, b) => b.priority - a.priority);
this.queriesItems = ActionMenu.createItems(queries, {
filter: q => q !== StructureSelectionQueries.current && !q.isHidden,
label: q => q.label,
category: q => q.category,
description: q => q.description
@@ -148,29 +172,29 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
return <>
<div className='msp-flex-row' style={{ background: 'none' }}>
<PureSelectControl title={`Picking Level`} param={StructureSelectionParams.granularity} name='granularity' value={granularity} onChange={this.setGranuality} isDisabled={this.isDisabled} />
<ToggleButton icon={Union} title={ActionHeader.get('add')} toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
<ToggleButton icon={Subtract} title={ActionHeader.get('remove')} toggle={this.toggleRemove} isSelected={this.state.action === 'remove'} disabled={this.isDisabled} />
<ToggleButton icon={Intersect} title={ActionHeader.get('intersect')} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
<ToggleButton icon={SetSvg} title={ActionHeader.get('set')} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
<PureSelectControl title={`Picking Level for selecting and highlighting`} param={StructureSelectionParams.granularity} name='granularity' value={granularity} onChange={this.setGranuality} isDisabled={this.isDisabled} />
<ToggleButton icon={Union} title={`${ActionHeader.get('add')}. Hold shift key to keep menu open.`} toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
<ToggleButton icon={Subtract} title={`${ActionHeader.get('remove')}. Hold shift key to keep menu open.`} toggle={this.toggleRemove} isSelected={this.state.action === 'remove'} disabled={this.isDisabled} />
<ToggleButton icon={Intersect} title={`${ActionHeader.get('intersect')}. Hold shift key to keep menu open.`} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
<ToggleButton icon={SetSvg} title={`${ActionHeader.get('set')}. Hold shift key to keep menu open.`} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
<ToggleButton icon={Brush} title='Color' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} style={{ marginLeft: '10px' }} />
<ToggleButton icon={CubeSvg} title='Create Representation' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
<IconButton svg={Remove} title='Subtract from Representations' onClick={this.subtract} disabled={this.isDisabled} />
<ToggleButton icon={Brush} title='Color Selection' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} style={{ marginLeft: '10px' }} />
<ToggleButton icon={CubeSvg} title='Create Representation of Selection' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
<IconButton svg={Remove} title='Subtract Selection from Representations' onClick={this.subtract} disabled={this.isDisabled} />
<IconButton svg={Restore} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} title={undoTitle} />
<IconButton svg={CancelOutlined} title='Turn selection mode off' onClick={this.turnOff} style={{ marginLeft: '10px' }} />
</div>
{(this.state.action && this.state.action !== 'color' && this.state.action !== 'add-repr') && <div className='msp-selection-viewport-controls-actions'>
<ActionMenu header={ActionHeader.get(this.state.action as StructureSelectionModifier)} items={this.queries} onSelect={this.selectQuery} noOffset />
<ActionMenu header={ActionHeader.get(this.state.action as StructureSelectionModifier)} title='Click to close.' items={this.queries} onSelect={this.selectQuery} noOffset />
</div>}
{this.state.action === 'color' && <div className='msp-selection-viewport-controls-actions'>
<ControlGroup header='Color' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleColor} topRightIcon={Close}>
<ControlGroup header='Color' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleColor} topRightIcon={Close}>
<ApplyColorControls onApply={this.toggleColor} />
</ControlGroup>
</div>}
{this.state.action === 'add-repr' && <div className='msp-selection-viewport-controls-actions'>
<ControlGroup header='Add Representation' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleAddRepr} topRightIcon={Close}>
<ControlGroup header='Add Representation' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleAddRepr} topRightIcon={Close}>
<AddComponentControls onApply={this.toggleAddRepr} forSelection />
</ControlGroup>
</div>}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -103,7 +103,7 @@ export function getProviderFromType(type: Type): Provider {
export async function open(name: string, filename: string, type: Type): Promise<Context> {
const provider = getProviderFromType(type);
const descriptor = await File.openRead(filename);
const file = FileHandle.fromDescriptor(descriptor);
const file = FileHandle.fromDescriptor(descriptor, filename);
const header = await provider.readHeader(name, file);
const data = { header, file, slices: void 0 as any };
return { data, provider };

View File

@@ -32,7 +32,7 @@ export async function createContext(filename: string, channels: Format.Context[]
}
const ctx: Data.Context = {
file: FileHandle.fromDescriptor(await File.createFile(filename)),
file: FileHandle.fromDescriptor(await File.createFile(filename), filename),
isPeriodic,
channels,
valueType,

View File

@@ -66,7 +66,7 @@ async function readHeader(filename: string | undefined, sourceId: string) {
let file: FileHandle | undefined;
try {
if (!filename) return void 0;
file = FileHandle.fromDescriptor(await File.openRead(filename));
file = FileHandle.fromDescriptor(await File.openRead(filename), filename);
const header = await DataFormat.readHeader(file);
return header.header;
} catch (e) {

View File

@@ -34,7 +34,7 @@ export default async function execute(params: Data.QueryParams, outputProvider:
let sourceFile: FileHandle | undefined;
try {
sourceFile = FileHandle.fromDescriptor(await File.openRead(params.sourceFilename));
sourceFile = FileHandle.fromDescriptor(await File.openRead(params.sourceFilename), params.sourceFilename);
await _execute(sourceFile, params, guid, outputProvider);
return true;
} catch (e) {