Compare commits

...

24 Commits

Author SHA1 Message Date
Alexander Rose
dfaa4dacdb 0.7.1-dev.7 2020-05-14 16:26:27 -07:00
Alexander Rose
f7adb8b589 fix icon typo 2020-05-14 16:24:50 -07:00
Alexander Rose
cb6b1bf19d 0.7.1-dev.6 2020-05-14 16:09:37 -07:00
Alexander Rose
27a4e1d7d9 icon tweaks 2020-05-14 16:06:38 -07:00
Alexander Rose
0cba88ad8c 0.7.1-dev.5 2020-05-14 15:50:50 -07:00
Alexander Rose
e535c4efa8 fix expected texture resource count 2020-05-14 15:49:08 -07:00
Alexander Rose
31f58ee110 export LocalStateSnapshots ui classes 2020-05-14 15:44:36 -07:00
Alexander Rose
feb167dcf8 fix case of DDmanHep and LDmanHep 2020-05-14 11:19:38 -07:00
Alexander Rose
1b53ea846b added 6 more common saccharide names 2020-05-14 10:56:58 -07:00
Alexander Rose
88b9be5fd1 Merge branch 'master' of https://github.com/molstar/molstar 2020-05-14 00:11:52 -07:00
Alexander Rose
89486ea9e2 clip objects & per group clipping
- variants: instance, pixel
2020-05-14 00:08:51 -07:00
Alexander Rose
86c09ead98 added Model helpers
- .hasCarbohydrate
- .hasProtein
- .hasNucleic
2020-05-13 16:40:22 -07:00
Alexander Rose
1f60d887a8 add gap even for consecutive residues if they are not connected 2020-05-13 14:18:24 -07:00
Alexander Rose
a672115505 Merge branch 'master' of https://github.com/molstar/molstar 2020-05-12 19:25:46 -07:00
Alexander Rose
8f54ea137d small tweaks 2020-05-12 19:24:53 -07:00
David Sehnal
4171008c3f apps/Viewer: removed unused async 2020-05-13 02:00:17 +02:00
David Sehnal
3a9c3780ac apps/viewer tweaks
- added loadStructureFromData
- fixed loadStructureFromUrl format param
2020-05-13 01:58:42 +02:00
David Sehnal
fe55f33bd1 mol-plugin-ui: make SVG icons "static" 2020-05-13 01:37:13 +02:00
Alexander Rose
71bc88c041 0.7.1-dev.4 2020-05-12 11:35:31 -07:00
Alexander Rose
a5aadfef0e removed material-ui dependency
- included used icons as svg
2020-05-12 11:29:42 -07:00
Alexander Rose
0b368ef804 removed bcif-static option from structure download action 2020-05-12 09:34:21 -07:00
Alexander Rose
f398993d33 0.7.1-dev.3 2020-05-11 12:21:46 -07:00
Alexander Rose
b6f59ca9c3 package updates
- updated all
- removed unused jest-raw-loader
- removed unused circular-dependency-plugin
- removed unused resolve-url-loader
2020-05-11 12:18:45 -07:00
Alexander Rose
c857c17bb4 removed custom name 'deposited' for structures from model
- use 'model' instead
2020-05-11 11:24:01 -07:00
93 changed files with 2895 additions and 3613 deletions

4527
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.1-dev.2",
"version": "0.7.1-dev.7",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -79,72 +79,68 @@
"contributors": [
"Alexander Rose <alexander.rose@weirdbyte.de>",
"David Sehnal <david.sehnal@gmail.com>",
"Sebastian Bittrich <sebastian.bittrich@rcsb.org>"
"Sebastian Bittrich <sebastian.bittrich@rcsb.org>",
"Ludovic Autin <autin@scripps.edu>"
],
"license": "MIT",
"devDependencies": {
"@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",
"@graphql-codegen/add": "^1.13.5",
"@graphql-codegen/cli": "^1.13.5",
"@graphql-codegen/time": "^1.13.5",
"@graphql-codegen/typescript": "^1.13.5",
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.5",
"@graphql-codegen/typescript-graphql-request": "^1.13.5",
"@graphql-codegen/typescript-operations": "^1.13.5",
"@types/cors": "^2.8.6",
"@typescript-eslint/eslint-plugin": "^2.29.0",
"@typescript-eslint/parser": "^2.29.0",
"@typescript-eslint/eslint-plugin": "^2.32.0",
"@typescript-eslint/parser": "^2.32.0",
"benchmark": "^2.1.4",
"circular-dependency-plugin": "^5.2.0",
"concurrently": "^5.1.0",
"concurrently": "^5.2.0",
"cpx2": "^2.0.0",
"css-loader": "^3.5.3",
"eslint": "^6.8.0",
"eslint": "^7.0.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.4.0",
"jest-raw-loader": "^1.0.1",
"http-server": "^0.12.3",
"jest": "^26.0.1",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.14.0",
"node-sass": "^4.14.1",
"raw-loader": "^4.0.1",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2",
"simple-git": "^1.132.0",
"style-loader": "^1.2.0",
"ts-jest": "^25.4.0",
"simple-git": "^2.4.0",
"style-loader": "^1.2.1",
"ts-jest": "^25.5.1",
"typescript": "^3.8.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-version-file-plugin": "^0.4.0"
},
"dependencies": {
"@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.2",
"@types/node": "^13.13.5",
"@types/node-fetch": "^2.5.7",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.6",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
"@types/swagger-ui-dist": "3.0.5",
"argparse": "^1.0.10",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"immer": "^6.0.3",
"immer": "^6.0.5",
"immutable": "^3.8.2",
"node-fetch": "^2.6.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"rxjs": "^6.5.5",
"swagger-ui-dist": "^3.25.0",
"tslib": "^1.11.1",
"swagger-ui-dist": "^3.25.2",
"tslib": "^1.11.2",
"util.promisify": "^1.0.1",
"xhr2": "^0.2.0"
}

View File

@@ -23,6 +23,7 @@ import { ObjectKeys } from '../../mol-util/type-helpers';
import { PluginState } from '../../mol-plugin/state';
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
require('mol-plugin-ui/skin/light.scss');
@@ -59,7 +60,7 @@ type ViewerOptions = typeof DefaultViewerOptions;
export class Viewer {
plugin: PluginContext
constructor(elementId: string, options: Partial<ViewerOptions> = {}) {
constructor(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
const o = { ...DefaultViewerOptions, ...options };
const spec: PluginSpec = {
@@ -99,21 +100,23 @@ export class Viewer {
]
};
const element = document.getElementById(elementId);
if (!element) throw new Error(`Could not get element with id '${elementId}'`);
const element = typeof elementOrId === 'string'
? document.getElementById(elementOrId)
: elementOrId;
if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
this.plugin = createPlugin(element, spec);
}
async setRemoteSnapshot(id: string) {
setRemoteSnapshot(id: string) {
const url = `${this.plugin.config.get(PluginConfig.State.CurrentServer)}/get/${id}`;
await PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
return PluginCommands.State.Snapshots.Fetch(this.plugin, { url });
}
async loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
await PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
loadSnapshotFromUrl(url: string, type: PluginState.SnapshotType) {
return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
}
async loadStructureFromUrl(url: string, format = 'cif', isBinary = false) {
loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
@@ -128,7 +131,13 @@ export class Viewer {
}));
}
async loadPdb(pdb: string) {
async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) {
const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel });
const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format);
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default');
}
loadPdb(pdb: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
const provider = this.plugin.config.get(PluginConfig.Download.DefaultPdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
@@ -148,7 +157,7 @@ export class Viewer {
}));
}
async loadPdbDev(pdbDev: string) {
loadPdbDev(pdbDev: string) {
const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
@@ -164,7 +173,7 @@ export class Viewer {
}));
}
async loadEmdb(emdb: string) {
loadEmdb(emdb: string) {
const provider = this.plugin.config.get(PluginConfig.Download.DefaultEmdbProvider)!;
return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
source: {

View File

@@ -69,15 +69,8 @@
$('format').value = format;
$('format').onchange = function (e) { format = e.target.value; }
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
// var format = 'pdb';
// var assemblyId = 'deposited';
BasicMolStarWrapper.init('app' /** or document.getElementById('app') */);
BasicMolStarWrapper.setBackground(0xffffff);
// BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId });
// BasicMolStarWrapper.toggleSpin();
addControl('Load Asym Unit', () => BasicMolStarWrapper.load({ url: url, format: format }));
addControl('Load Assembly', () => BasicMolStarWrapper.load({ url: url, format: format, assemblyId: assemblyId }));

View File

@@ -56,7 +56,13 @@ class BasicWrapper {
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default', {
structure: assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } },
structure: assemblyId ? {
name: 'assembly',
params: { id: assemblyId }
} : {
name: 'model',
params: { }
},
showUnitcell: false,
representationPreset: 'auto'
});

View File

@@ -105,7 +105,7 @@ class LightingDemo {
const data = await this.plugin.builders.data.download({ url: Asset.Url(url), isBinary }, { state: { isGhost: true } });
const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
const model = await this.plugin.builders.structure.createModel(trajectory);
const structure = await this.plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } });
const structure = await this.plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'model', params: { } });
const polymer = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer');
if (polymer) await this.plugin.builders.structure.representation.addRepresentation(polymer, { type: 'spacefill', color: 'illustrative' });

View File

@@ -88,10 +88,6 @@
$('isBinary').checked = isBinary;
$('isBinary').onchange = function (e) { isBinary = !!e.target.checked; };
// var url = 'https://www.ebi.ac.uk/pdbe/entry-files/pdb' + pdbId + '.ent';
// var format = 'pdb';
// var assemblyId = 'deposited';
function loadAndSnapshot(params) {
PluginWrapper.load(params).then(() => {
setTimeout(() => snapshot = PluginWrapper.plugin.state.getSnapshot({ canvas3d: false /* do not save spinning state */ }), 500);

View File

@@ -89,9 +89,12 @@ class MolStarProteopediaWrapper {
private structure(assemblyId: string) {
const model = this.state.build().to(StateElements.Model);
const props = {
type: {
type: assemblyId ? {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
params: { id: assemblyId }
} : {
name: 'model' as const,
params: { }
}
};
@@ -196,7 +199,7 @@ class MolStarProteopediaWrapper {
private emptyLoadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
private loadedParams: LoadParams = { url: '', format: 'cif', isBinary: false, assemblyId: '' };
async load({ url, format = 'cif', assemblyId = 'deposited', isBinary = false, representationStyle }: LoadParams) {
async load({ url, format = 'cif', assemblyId = '', isBinary = false, representationStyle }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.data;
@@ -220,9 +223,12 @@ class MolStarProteopediaWrapper {
const info = await this.doInfo(true);
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
const props = {
type: {
type: assemblyId ? {
name: 'assembly' as const,
params: { id: asmId || 'deposited' }
params: { id: asmId }
} : {
name: 'model' as const,
params: { }
}
};
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));

View File

@@ -35,7 +35,7 @@ export function isBiologicalAssembly(structure: Structure): boolean {
const mmcif = structure.models[0].sourceData.data.db;
if (!mmcif.pdbx_struct_assembly.details.isDefined) return false;
const id = structure.units[0].conformation.operator.assembly?.id || '';
if (id === '' || id === 'deposited') return true;
if (id === '') return true;
const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id);
if (indices.length !== 1) return false;
const details = mmcif.pdbx_struct_assembly.details.value(indices[0]);
@@ -63,7 +63,7 @@ export namespace AssemblySymmetry {
const client = new GraphQLClient(props.serverUrl, ctx.assetManager);
const variables: AssemblySymmetryQueryVariables = {
assembly_id: structure.units[0].conformation.operator.assembly?.id || 'deposited',
assembly_id: structure.units[0].conformation.operator.assembly?.id || '',
entry_id: structure.units[0].model.entryId
};
const result = await client.request(ctx.runtime, query, variables);

View File

@@ -16,8 +16,7 @@ import { StateAction, StateSelection } from '../../../mol-state';
import { PluginStateObject } from '../../../mol-plugin-state/objects';
import { PluginContext } from '../../../mol-plugin/context';
import { Task } from '../../../mol-task';
import Check from '@material-ui/icons/Check';
import Extension from '@material-ui/icons/Extension';
import { ExtensionSvg, CheckSvg } from '../../../mol-plugin-ui/controls/icons';
interface AssemblySymmetryControlState extends CollapsableState {
isBusy: boolean
@@ -30,7 +29,7 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
isCollapsed: false,
isBusy: false,
isHidden: true,
brand: { accent: 'cyan', svg: Extension }
brand: { accent: 'cyan', svg: ExtensionSvg }
};
}
@@ -62,7 +61,7 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
renderEnable() {
const pivot = this.pivot;
if (!pivot.cell.parent) return null;
return <ApplyActionControl state={pivot.cell.parent} action={EnableAssemblySymmetry3D} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={{ header: 'Enable', icon: Check }} />;
return <ApplyActionControl state={pivot.cell.parent} action={EnableAssemblySymmetry3D} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={{ header: 'Enable', icon: CheckSvg }} />;
}
renderNoSymmetries() {

View File

@@ -322,14 +322,14 @@ export const ValidationReportGeometryQualityPreset = StructureRepresentationPres
const clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' });
const { update, builder, typeParams, color } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
let clashesBallAndStick, clashesSnfg3d;
let clashesBallAndStick, clashesRepr;
if (representations) {
clashesBallAndStick = builder.buildRepresentation(update, clashes, { type: 'ball-and-stick', typeParams, color: colorTheme }, { tag: 'clashes-ball-and-stick' });
clashesSnfg3d = builder.buildRepresentation<any>(update, clashes, { type: ClashesRepresentationProvider.name, typeParams, color }, { tag: 'clashes-snfg-3d' });
clashesRepr = builder.buildRepresentation<any>(update, clashes, { type: ClashesRepresentationProvider.name, typeParams, color }, { tag: 'clashes-repr' });
}
await update.commit({ revertOnError: true });
return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesSnfg3d } };
return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesRepr } };
}
});

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../mol-util/value-cell';
import { Vec2 } from '../../mol-math/linear-algebra';
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
import { Clipping } from '../../mol-theme/clipping';
export type ClippingData = {
dClipObjectCount: ValueCell<number>,
dClipVariant: ValueCell<string>,
tClipping: ValueCell<TextureImage<Uint8Array>>
uClippingTexDim: ValueCell<Vec2>
dClipping: ValueCell<boolean>,
}
export function applyClippingGroups(array: Uint8Array, start: number, end: number, groups: Clipping.Groups) {
for (let i = start; i < end; ++i) {
array[i] = groups;
}
return true;
}
export function clearClipping(array: Uint8Array, start: number, end: number) {
array.fill(0, start, end);
}
export function createClipping(count: number, clippingData?: ClippingData): ClippingData {
const clipping = createTextureImage(Math.max(1, count), 1, Uint8Array, clippingData && clippingData.tClipping.ref.value.array);
if (clippingData) {
ValueCell.update(clippingData.tClipping, clipping);
ValueCell.update(clippingData.uClippingTexDim, Vec2.create(clipping.width, clipping.height));
ValueCell.update(clippingData.dClipping, count > 0);
return clippingData;
} else {
return {
dClipObjectCount: ValueCell.create(0),
dClipVariant: ValueCell.create('instance'),
tClipping: ValueCell.create(clipping),
uClippingTexDim: ValueCell.create(Vec2.create(clipping.width, clipping.height)),
dClipping: ValueCell.create(count > 0),
};
}
}
const emptyClippingTexture = { array: new Uint8Array(1), width: 1, height: 1 };
export function createEmptyClipping(clippingData?: ClippingData): ClippingData {
if (clippingData) {
ValueCell.update(clippingData.tClipping, emptyClippingTexture);
ValueCell.update(clippingData.uClippingTexDim, Vec2.create(1, 1));
return clippingData;
} else {
return {
dClipObjectCount: ValueCell.create(0),
dClipVariant: ValueCell.create('instance'),
tClipping: ValueCell.create(emptyClippingTexture),
uClippingTexDim: ValueCell.create(Vec2.create(1, 1)),
dClipping: ValueCell.create(false),
};
}
}

View File

@@ -12,7 +12,7 @@ import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Texture } from '../../../mol-gl/webgl/texture';
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
import { Mat4, Vec2, Vec3 } from '../../../mol-math/linear-algebra';
import { Mat4, Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
import { Theme } from '../../../mol-theme/theme';
import { ValueCell } from '../../../mol-util';
import { Color } from '../../../mol-util/color';
@@ -26,6 +26,7 @@ import { createEmptyOverpaint } from '../overpaint-data';
import { TransformData } from '../transform-data';
import { createEmptyTransparency } from '../transparency-data';
import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
import { createEmptyClipping } from '../clipping-data';
const VolumeBox = Box();
const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][];
@@ -140,6 +141,7 @@ export namespace DirectVolume {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount };
@@ -156,6 +158,7 @@ export namespace DirectVolume {
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
...BaseGeometry.createValues(props, counts),
@@ -163,6 +166,7 @@ export namespace DirectVolume {
elements: ValueCell.create(VolumeBox.indices as Uint32Array),
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
uIsoValue: ValueCell.create(props.isoValueNorm),
uBboxMin: bboxMin,
@@ -204,6 +208,7 @@ export namespace DirectVolume {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}

View File

@@ -9,7 +9,7 @@ import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { RenderableState } from '../../../mol-gl/renderable';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere, TextureImage } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';
import { Vec2 } from '../../../mol-math/linear-algebra';
import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
import { Theme } from '../../../mol-theme/theme';
import { ValueCell } from '../../../mol-util';
import { Color } from '../../../mol-util/color';
@@ -23,6 +23,7 @@ import { TransformData } from '../transform-data';
import { createEmptyTransparency } from '../transparency-data';
import { ImageValues } from '../../../mol-gl/renderable/image';
import { fillSerial } from '../../../mol-util/array';
import { createEmptyClipping } from '../clipping-data';
const QuadIndices = new Uint32Array([
0, 1, 2,
@@ -137,6 +138,7 @@ namespace Image {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: QuadIndices.length, groupCount, instanceCount };
@@ -148,6 +150,7 @@ namespace Image {
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
...BaseGeometry.createValues(props, counts),
@@ -159,6 +162,7 @@ namespace Image {
aGroup: ValueCell.create(fillSerial(new Float32Array(4))),
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
dInterpolation: ValueCell.create(props.interpolation),
@@ -188,6 +192,7 @@ namespace Image {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}

View File

@@ -5,7 +5,7 @@
*/
import { ValueCell } from '../../../mol-util';
import { Mat4 } from '../../../mol-math/linear-algebra';
import { Mat4, Vec4 } from '../../../mol-math/linear-algebra';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
@@ -25,6 +25,7 @@ import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
import { createEmptyClipping } from '../clipping-data';
/** Wide line */
export interface Lines {
@@ -184,6 +185,7 @@ export namespace Lines {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount };
@@ -198,11 +200,13 @@ export namespace Lines {
elements: lines.indexBuffer,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
...color,
...size,
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
...BaseGeometry.createValues(props, counts),
@@ -234,6 +238,7 @@ export namespace Lines {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}
}

View File

@@ -6,7 +6,7 @@
*/
import { ValueCell } from '../../../mol-util';
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
import { Vec3, Mat4, Mat3, Vec4 } from '../../../mol-math/linear-algebra';
import { Sphere3D } from '../../../mol-math/geometry';
import { transformPositionArray, transformDirectionArray, computeIndexedVertexNormals, GroupMapping, createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
@@ -23,6 +23,7 @@ import { Color } from '../../../mol-util/color';
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { createEmptyClipping } from '../clipping-data';
export interface Mesh {
readonly kind: 'mesh',
@@ -355,6 +356,7 @@ export namespace Mesh {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount };
@@ -368,10 +370,12 @@ export namespace Mesh {
elements: mesh.indexBuffer,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
...color,
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
...BaseGeometry.createValues(props, counts),
@@ -405,6 +409,7 @@ export namespace Mesh {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}
}

View File

@@ -5,7 +5,7 @@
*/
import { ValueCell } from '../../../mol-util';
import { Mat4 } from '../../../mol-math/linear-algebra';
import { Mat4, Vec4 } from '../../../mol-math/linear-algebra';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
@@ -24,6 +24,7 @@ import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
import { createEmptyClipping } from '../clipping-data';
/** Point cloud */
export interface Points {
@@ -143,6 +144,7 @@ export namespace Points {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: points.pointCount, groupCount, instanceCount };
@@ -154,11 +156,13 @@ export namespace Points {
aGroup: points.groupBuffer,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
...color,
...size,
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
...BaseGeometry.createValues(props, counts),
@@ -192,6 +196,7 @@ export namespace Points {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}

View File

@@ -22,6 +22,8 @@ import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
import { GroupMapping, createGroupMapping } from '../../util';
import { createEmptyClipping } from '../clipping-data';
import { Vec4 } from '../../../mol-math/linear-algebra';
export interface Spheres {
readonly kind: 'spheres',
@@ -147,6 +149,7 @@ export namespace Spheres {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount };
@@ -161,11 +164,13 @@ export namespace Spheres {
elements: spheres.indexBuffer,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
...color,
...size,
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
padding: ValueCell.create(padding),
@@ -200,6 +205,7 @@ export namespace Spheres {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
ValueCell.update(values.padding, padding);
}

View File

@@ -18,7 +18,7 @@ import { Sphere3D } from '../../../mol-math/geometry';
import { TextureImage, createTextureImage, calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { TextValues } from '../../../mol-gl/renderable/text';
import { Color } from '../../../mol-util/color';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { Vec3, Vec4 } from '../../../mol-math/linear-algebra';
import { FontAtlasParams } from './font-atlas';
import { RenderableState } from '../../../mol-gl/renderable';
import { clamp } from '../../../mol-math/interpolate';
@@ -28,6 +28,7 @@ import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
import { GroupMapping, createGroupMapping } from '../../util';
import { createEmptyClipping } from '../clipping-data';
type TextAttachment = (
'bottom-left' | 'bottom-center' | 'bottom-right' |
@@ -195,6 +196,7 @@ export namespace Text {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: text.charCount * 2 * 3, groupCount, instanceCount };
@@ -210,11 +212,13 @@ export namespace Text {
elements: text.indexBuffer,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
...color,
...size,
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
aTexCoord: text.tcoordBuffer,
@@ -269,6 +273,7 @@ export namespace Text {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
ValueCell.update(values.padding, padding);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 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>
*/
@@ -20,8 +20,9 @@ import { createEmptyTransparency } from '../transparency-data';
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Texture } from '../../../mol-gl/webgl/texture';
import { Vec2 } from '../../../mol-math/linear-algebra';
import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
import { fillSerial } from '../../../mol-util/array';
import { createEmptyClipping } from '../clipping-data';
export interface TextureMesh {
readonly kind: 'texture-mesh',
@@ -93,6 +94,7 @@ export namespace TextureMesh {
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: textureMesh.vertexCount, groupCount, instanceCount };
@@ -107,11 +109,13 @@ export namespace TextureMesh {
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount))),
boundingSphere: ValueCell.create(transformBoundingSphere),
invariantBoundingSphere: ValueCell.create(Sphere3D.clone(textureMesh.boundingSphere)),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(textureMesh.boundingSphere)),
...color,
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
...BaseGeometry.createValues(props, counts),
@@ -149,6 +153,7 @@ export namespace TextureMesh {
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}
}

View File

@@ -7,7 +7,7 @@
import { createGl } from './gl.shim';
import { Camera } from '../../mol-canvas3d/camera';
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
import { Vec3, Mat4, Vec4 } from '../../mol-math/linear-algebra';
import { ValueCell } from '../../mol-util';
import Renderer from '../renderer';
@@ -24,6 +24,7 @@ import { Color } from '../../mol-util/color';
import { Sphere3D } from '../../mol-math/geometry';
import { createEmptyOverpaint } from '../../mol-geo/geometry/overpaint-data';
import { createEmptyTransparency } from '../../mol-geo/geometry/transparency-data';
import { createEmptyClipping } from '../../mol-geo/geometry/clipping-data';
function createRenderer(gl: WebGLRenderingContext) {
const ctx = createContext(gl);
@@ -43,6 +44,7 @@ function createPoints() {
const marker = createEmptyMarkers();
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const aTransform = ValueCell.create(new Float32Array(16));
const m4 = Mat4.identity();
@@ -63,10 +65,12 @@ function createPoints() {
...size,
...overpaint,
...transparency,
...clipping,
uAlpha: ValueCell.create(1.0),
uInstanceCount: ValueCell.create(1),
uGroupCount: ValueCell.create(3),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere.ref.value)),
alpha: ValueCell.create(1.0),
drawCount: ValueCell.create(3),
@@ -124,7 +128,7 @@ describe('renderer', () => {
scene.add(points);
scene.commit();
expect(ctx.stats.resourceCounts.attribute).toBe(4);
expect(ctx.stats.resourceCounts.texture).toBe(5);
expect(ctx.stats.resourceCounts.texture).toBe(6);
expect(ctx.stats.resourceCounts.vertexArray).toBe(5);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).toBe(10);

View File

@@ -29,8 +29,15 @@ export const DirectVolumeSchema = {
dTransparency: DefineSpec('boolean'),
dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
dClipObjectCount: DefineSpec('number'),
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
uClippingTexDim: UniformSpec('v2'),
tClipping: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dClipping: DefineSpec('boolean'),
uInstanceCount: UniformSpec('i'),
uGroupCount: UniformSpec('i'),
uInvariantBoundingSphere: UniformSpec('v4'),
aInstance: AttributeSpec('float32', 1, 1),
aTransform: AttributeSpec('float32', 16, 1),

View File

@@ -1,22 +1,21 @@
/**
* Copyright (c) 2018-2019 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../mol-util';
import { AttributeItemSize, ElementsKind, AttributeValues, AttributeKind } from '../webgl/buffer';
import { UniformKind, UniformValues } from '../webgl/uniform';
import { AttributeItemSize, ElementsKind, AttributeValues, AttributeKind, DataTypeArrayType } from '../webgl/buffer';
import { UniformKind, UniformValues, UniformKindValue } from '../webgl/uniform';
import { DefineKind, DefineValues } from '../shader-code';
import { Vec2, Vec3, Vec4, Mat3, Mat4 } from '../../mol-math/linear-algebra';
import { TextureImage, TextureVolume } from './util';
import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind, Texture } from '../webgl/texture';
import { Mat4 } from '../../mol-math/linear-algebra';
import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind, TextureKindValue } from '../webgl/texture';
import { Sphere3D } from '../../mol-math/geometry';
export type ValueKindType = {
'number': number
'string': string
'boolean': string
'boolean': boolean
'any': any
'm4': Mat4,
@@ -27,40 +26,7 @@ export type ValueKind = keyof ValueKindType
//
export type KindValue = {
'f': number
'i': number
'v2': Vec2
'v3': Vec3
'v4': Vec4
'm3': Mat3
'm4': Mat4
't': number
'uint8': Uint8Array
'int8': Int8Array
'uint16': Uint16Array
'int16': Int16Array
'uint32': Uint32Array
'int32': Int32Array
'float32': Float32Array
'image-uint8': TextureImage<Uint8Array>
'image-float32': TextureImage<Float32Array>
'image-depth': TextureImage<Uint8Array> // TODO should be Uint32Array
'volume-uint8': TextureVolume<Uint8Array>
'volume-float32': TextureVolume<Float32Array>
'texture': Texture
'texture2d': Texture
'texture3d': Texture
'number': number
'string': string
'boolean': boolean
'any': any
'sphere': Sphere3D
}
export type KindValue = UniformKindValue & DataTypeArrayType & TextureKindValue & ValueKindType
export type Values<S extends RenderableSchema> = { readonly [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> }
@@ -163,6 +129,11 @@ export const GlobalUniformSchema = {
uTransparentBackground: UniformSpec('i'),
uClipObjectType: UniformSpec('i[]'),
uClipObjectPosition: UniformSpec('v3[]'),
uClipObjectRotation: UniformSpec('v4[]'),
uClipObjectScale: UniformSpec('v3[]'),
// all the following could in principle be per object
// as a kind of 'material' parameter set
// would need to test performance implications
@@ -228,21 +199,31 @@ export type OverpaintSchema = typeof OverpaintSchema
export type OverpaintValues = Values<OverpaintSchema>
export const TransparencySchema = {
// aTransparency: AttributeSpec('float32', 1, 0), // TODO
uTransparencyTexDim: UniformSpec('v2'),
tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dTransparency: DefineSpec('boolean'),
// dTransparencyType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), // TODO
dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
} as const;
export type TransparencySchema = typeof TransparencySchema
export type TransparencyValues = Values<TransparencySchema>
export const ClippingSchema = {
dClipObjectCount: DefineSpec('number'),
dClipVariant: DefineSpec('string', ['instance', 'pixel']),
uClippingTexDim: UniformSpec('v2'),
tClipping: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dClipping: DefineSpec('boolean'),
} as const;
export type ClippingSchema = typeof ClippingSchema
export type ClippingValues = Values<ClippingSchema>
export const BaseSchema = {
...ColorSchema,
...MarkerSchema,
...OverpaintSchema,
...TransparencySchema,
...ClippingSchema,
aInstance: AttributeSpec('float32', 1, 1),
aGroup: AttributeSpec('float32', 1, 0),
@@ -258,6 +239,7 @@ export const BaseSchema = {
uAlpha: UniformSpec('f', true),
uInstanceCount: UniformSpec('i'),
uGroupCount: UniformSpec('i'),
uInvariantBoundingSphere: UniformSpec('v4'),
drawCount: ValueSpec('number'),
instanceCount: ValueSpec('number'),
@@ -272,7 +254,7 @@ export const BaseSchema = {
/** additional per-instance transform, see aTransform */
extraTransform: ValueSpec('float32'),
/** bounding sphere taking aTransform into account */
/** bounding sphere taking aTransform into account and encompases all instances */
boundingSphere: ValueSpec('sphere'),
/** bounding sphere NOT taking aTransform into account */
invariantBoundingSphere: ValueSpec('sphere'),

View File

@@ -9,13 +9,15 @@ import { Camera } from '../mol-canvas3d/camera';
import Scene from './scene';
import { WebGLContext } from './webgl/context';
import { Mat4, Vec3, Vec4, Vec2 } from '../mol-math/linear-algebra';
import { Mat4, Vec3, Vec4, Vec2, Quat } from '../mol-math/linear-algebra';
import { Renderable } from './renderable';
import { Color } from '../mol-util/color';
import { ValueCell } from '../mol-util';
import { ValueCell, deepEqual } from '../mol-util';
import { RenderableValues, GlobalUniformValues, BaseValues } from './renderable/schema';
import { GraphicsRenderVariant } from './webgl/render-item';
import { ParamDefinition as PD } from '../mol-util/param-definition';
import { Clipping } from '../mol-theme/clipping';
import { stringToWords } from '../mol-util/string';
export interface RendererStats {
programCount: number
@@ -71,6 +73,19 @@ export const RendererParams = {
metallic: PD.Group({}),
plastic: PD.Group({}),
}, { label: 'Lighting', description: 'Style in which the 3D scene is rendered/lighted' }),
clip: PD.Group({
variant: PD.Select('instance', PD.arrayToOptions<Clipping.Variant>(['instance', 'pixel'])),
objects: PD.ObjectList({
type: PD.Select('plane', PD.objectToOptions(Clipping.Type, t => stringToWords(t))),
position: PD.Vec3(Vec3()),
rotation: PD.Group({
axis: PD.Vec3(Vec3.create(1, 0, 0)),
angle: PD.Numeric(0, { min: -180, max: 180, step: 0.1 }),
}, { isExpanded: true }),
scale: PD.Vec3(Vec3.create(1, 1, 1)),
}, o => stringToWords(o.type))
})
};
export type RendererProps = PD.Values<typeof RendererParams>
@@ -106,11 +121,44 @@ function getStyle(props: RendererProps['style']) {
}
}
type Clip = {
variant: Clipping.Variant
objects: {
count: number
type: number[]
position: number[]
rotation: number[]
scale: number[]
}
}
const tmpQuat = Quat();
function getClip(props: RendererProps['clip'], clip?: Clip): Clip {
const { type, position, rotation, scale } = clip?.objects || {
type: (new Array(5)).fill(1),
position: (new Array(5 * 3)).fill(0),
rotation: (new Array(5 * 4)).fill(0),
scale: (new Array(5 * 3)).fill(1),
};
for (let i = 0, il = props.objects.length; i < il; ++i) {
const p = props.objects[i];
type[i] = Clipping.Type[p.type];
Vec3.toArray(p.position, position, i * 3);
Quat.toArray(Quat.setAxisAngle(tmpQuat, p.rotation.axis, p.rotation.angle), rotation, i * 4);
Vec3.toArray(p.scale, scale, i * 3);
}
return {
variant: props.variant,
objects: { count: props.objects.length, type, position, rotation, scale }
};
}
namespace Renderer {
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
const { gl, state, stats } = ctx;
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
const style = getStyle(p.style);
const clip = getClip(p.clip);
const viewport = Viewport();
const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor);
@@ -151,6 +199,11 @@ namespace Renderer {
uFogColor: ValueCell.create(bgColor),
uTransparentBackground: ValueCell.create(0),
uClipObjectType: ValueCell.create(clip.objects.type),
uClipObjectPosition: ValueCell.create(clip.objects.position),
uClipObjectRotation: ValueCell.create(clip.objects.rotation),
uClipObjectScale: ValueCell.create(clip.objects.scale),
// the following are general 'material' uniforms
uLightIntensity: ValueCell.create(style.lightIntensity),
uAmbientIntensity: ValueCell.create(style.ambientIntensity),
@@ -177,6 +230,17 @@ namespace Renderer {
return;
}
let definesNeedUpdate = false;
if (r.values.dClipObjectCount.ref.value !== clip.objects.count) {
ValueCell.update(r.values.dClipObjectCount, clip.objects.count);
definesNeedUpdate = true;
}
if (r.values.dClipVariant.ref.value !== clip.variant) {
ValueCell.update(r.values.dClipVariant, clip.variant);
definesNeedUpdate = true;
}
if (definesNeedUpdate) r.update();
const program = r.getProgram(variant);
if (state.currentProgramId !== program.id) {
// console.log('new program')
@@ -341,6 +405,15 @@ namespace Renderer {
ValueCell.updateIfChanged(globalUniforms.uRoughness, style.roughness);
ValueCell.updateIfChanged(globalUniforms.uReflectivity, style.reflectivity);
}
if (props.clip !== undefined && !deepEqual(props.clip, p.clip)) {
p.clip = props.clip;
Object.assign(clip, getClip(props.clip, clip));
ValueCell.update(globalUniforms.uClipObjectPosition, clip.objects.position);
ValueCell.update(globalUniforms.uClipObjectRotation, clip.objects.rotation);
ValueCell.update(globalUniforms.uClipObjectScale, clip.objects.scale);
ValueCell.update(globalUniforms.uClipObjectType, clip.objects.type);
}
},
setViewport: (x: number, y: number, width: number, height: number) => {
gl.viewport(x, y, width, height);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -34,6 +34,7 @@ import apply_fog from './shader/chunks/apply-fog.glsl';
import apply_interior_color from './shader/chunks/apply-interior-color.glsl';
import apply_light_color from './shader/chunks/apply-light-color.glsl';
import apply_marker_color from './shader/chunks/apply-marker-color.glsl';
import assign_clipping_varying from './shader/chunks/assign-clipping-varying.glsl';
import assign_color_varying from './shader/chunks/assign-color-varying.glsl';
import assign_group from './shader/chunks/assign-group.glsl';
import assign_marker_varying from './shader/chunks/assign-marker-varying.glsl';
@@ -41,8 +42,11 @@ import assign_material_color from './shader/chunks/assign-material-color.glsl';
import assign_position from './shader/chunks/assign-position.glsl';
import assign_size from './shader/chunks/assign-size.glsl';
import check_picking_alpha from './shader/chunks/check-picking-alpha.glsl';
import clip_instance from './shader/chunks/clip-instance.glsl';
import clip_pixel from './shader/chunks/clip-pixel.glsl';
import color_frag_params from './shader/chunks/color-frag-params.glsl';
import color_vert_params from './shader/chunks/color-vert-params.glsl';
import common_clip from './shader/chunks/common-clip.glsl';
import common_frag_params from './shader/chunks/common-frag-params.glsl';
import common_vert_params from './shader/chunks/common-vert-params.glsl';
import common from './shader/chunks/common.glsl';
@@ -59,6 +63,7 @@ const ShaderChunks: { [k: string]: string } = {
apply_interior_color,
apply_light_color,
apply_marker_color,
assign_clipping_varying,
assign_color_varying,
assign_group,
assign_marker_varying,
@@ -66,8 +71,11 @@ const ShaderChunks: { [k: string]: string } = {
assign_position,
assign_size,
check_picking_alpha,
clip_instance,
clip_pixel,
color_frag_params,
color_vert_params,
common_clip,
common_frag_params,
common_vert_params,
common,

View File

@@ -0,0 +1,5 @@
export default `
#if dClipObjectCount != 0 && defined(dClipping)
vClipping = readFromTexture(tClipping, aInstance * float(uGroupCount) + group, uClippingTexDim).a;
#endif
`;

View File

@@ -1,11 +1,14 @@
export default `
mat4 modelView = uView * uModel * aTransform;
mat4 model = uModel * aTransform;
mat4 modelView = uView * model;
#ifdef dGeoTexture
vec3 position = readFromTexture(tPositionGroup, aGroup, uGeoTexDim).xyz;
#else
vec3 position = aPosition;
#endif
vec4 mvPosition = modelView * vec4(position, 1.0);
vec4 position4 = vec4(position, 1.0);
vModelPosition = (model * position4).xyz;
vec4 mvPosition = modelView * position4;
vViewPosition = mvPosition.xyz;
gl_Position = uProjection * mvPosition;
`;

View File

@@ -0,0 +1,13 @@
export default `
#if defined(dClipVariant_instance) && dClipObjectCount != 0
int flag = 0;
#if defined(dClipping)
flag = int(floor(vClipping * 255.0 + 0.5));
#endif
vec4 mCenter = model * vec4(uInvariantBoundingSphere.xyz, 1.0);
if (clipTest(vec4(mCenter.xyz, uInvariantBoundingSphere.w), flag))
// move out of [ -w, +w ] to 'discard' in vert shader
gl_Position.z = 2.0 * gl_Position.w;
#endif
`;

View File

@@ -0,0 +1,11 @@
export default `
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
int flag = 0;
#if defined(dClipping)
flag = int(floor(vClipping * 255.0 + 0.5));
#endif
if (clipTest(vec4(vModelPosition, 0.0), flag))
discard;
#endif
`;

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Ludovic Autin <autin@scripps.edu>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export default `
#if dClipObjectCount != 0
vec3 quaternionTransform(vec4 q, vec3 v) {
vec3 t = 2.0 * cross(q.xyz, v);
return v + q.w * t + cross(q.xyz, t);
}
vec4 computePlane(vec3 normal, vec3 inPoint) {
return vec4(normalize(normal), -dot(normal, inPoint));
}
float planeSD(vec4 plane, vec3 center) {
return -dot(plane.xyz, center - plane.xyz * -plane.w);
}
float sphereSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
return (
length(quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position) / size) - 1.0
) * min(min(size.x, size.y), size.z);
}
float cubeSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
vec3 d = abs(quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position)) - size;
return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
}
float cylinderSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
vec3 t = quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position);
vec2 d = abs(vec2(length(t.xz), t.y)) - size.xy;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}
float infiniteConeSD(vec3 position, vec4 rotation, vec3 size, vec3 center) {
vec3 t = quaternionTransform(vec4(-rotation.x, -rotation.y, -rotation.z, rotation.w), center - position);
float q = length(t.xy);
return dot(size.xy, vec2(q, t.z));
}
float getSignedDistance(vec3 center, int type, vec3 position, vec4 rotation, vec3 scale) {
if (type == 1) {
vec3 normal = quaternionTransform(rotation, vec3(0.0, 1.0, 0.0));
vec4 plane = computePlane(normal, position);
return planeSD(plane, center);
} else if (type == 2) {
return sphereSD(position, rotation, scale * 0.5, center);
} else if (type == 3) {
return cubeSD(position, rotation, scale * 0.5, center);
} else if (type == 4) {
return cylinderSD(position, rotation, scale * 0.5, center);
} else if (type == 5) {
return infiniteConeSD(position, rotation, scale * 0.5, center);
} else {
return 0.1;
}
}
#if __VERSION__ != 300
// 8-bit
int bitwiseAnd(int a, int b) {
int d = 128;
int result = 0;
for (int i = 0; i < 8; ++i) {
if (d <= 0) break;
if (a >= d && b >= d) result += d;
if (a >= d) a -= d;
if (b >= d) b -= d;
d /= 2;
}
return result;
}
bool hasBit(int mask, int bit) {
return bitwiseAnd(mask, bit) == 0;
}
#else
bool hasBit(int mask, int bit) {
return (mask & bit) == 0;
}
#endif
// flag is a bit-flag for clip-objects to ignore (note, object ids start at 1 not 0)
bool clipTest(vec4 sphere, int flag) {
for (int i = 0; i < dClipObjectCount; ++i) {
if (flag == 0 || hasBit(flag, i + 1)) {
// TODO take sphere radius into account?
if (getSignedDistance(sphere.xyz, uClipObjectType[i], uClipObjectPosition[i], uClipObjectRotation[i], uClipObjectScale[i]) <= 0.0)
return true;
}
}
return false;
}
#endif
`;

View File

@@ -3,6 +3,21 @@ uniform int uObjectId;
uniform int uInstanceCount;
uniform int uGroupCount;
#if dClipObjectCount != 0
uniform int uClipObjectType[dClipObjectCount];
uniform vec3 uClipObjectPosition[dClipObjectCount];
uniform vec4 uClipObjectRotation[dClipObjectCount];
uniform vec3 uClipObjectScale[dClipObjectCount];
#if defined(dClipping)
#if __VERSION__ != 300
varying float vClipping;
#else
flat in float vClipping;
#endif
#endif
#endif
uniform vec3 uHighlightColor;
uniform vec3 uSelectColor;
#if __VERSION__ != 300
@@ -11,6 +26,7 @@ uniform vec3 uSelectColor;
flat in float vMarker;
#endif
varying vec3 vModelPosition;
varying vec3 vViewPosition;
uniform vec2 uViewOffset;

View File

@@ -5,6 +5,24 @@ uniform vec3 uCameraPosition;
uniform int uObjectId;
uniform int uInstanceCount;
uniform int uGroupCount;
uniform vec4 uInvariantBoundingSphere;
#if dClipObjectCount != 0
uniform int uClipObjectType[dClipObjectCount];
uniform vec3 uClipObjectPosition[dClipObjectCount];
uniform vec4 uClipObjectRotation[dClipObjectCount];
uniform vec3 uClipObjectScale[dClipObjectCount];
#if defined(dClipping)
uniform vec2 uClippingTexDim;
uniform sampler2D tClipping;
#if __VERSION__ != 300
varying float vClipping;
#else
flat out float vClipping;
#endif
#endif
#endif
uniform vec2 uMarkerTexDim;
uniform sampler2D tMarker;
@@ -14,5 +32,6 @@ uniform sampler2D tMarker;
flat out float vMarker;
#endif
varying vec3 vModelPosition;
varying vec3 vViewPosition;
`;

View File

@@ -13,8 +13,11 @@ precision highp int;
#include color_frag_params
#include light_frag_params
#include normal_frag_params
#include common_clip
void main() {
#include clip_pixel
// Workaround for buggy gl_FrontFacing (e.g. on some integrated Intel GPUs)
#if defined(enabledStandardDerivatives)
vec3 fdx = dFdx(vViewPosition);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -12,6 +12,7 @@ precision highp int;
#include read_from_texture
#include common_vert_params
#include color_vert_params
#include common_clip
#ifdef dGeoTexture
uniform vec2 uGeoTexDim;
@@ -34,7 +35,9 @@ void main(){
#include assign_group
#include assign_color_varying
#include assign_marker_varying
#include assign_clipping_varying
#include assign_position
#include clip_instance
#ifdef dGeoTexture
vec3 normal = readFromTexture(tNormal, aGroup, uGeoTexDim).xyz;

View File

@@ -64,7 +64,7 @@ void main(void){
vec4 mvCorner = vec4(mvPosition.xyz, 1.0);
if (vTexCoord.x == 10.0) { // indicates background plane
// move a bit to the back, tkaing ditsnace to camera into account to avoid z-fighting
// move a bit to the back, taking distance to camera into account to avoid z-fighting
offsetZ -= 0.001 * distance(uCameraPosition, (uProjection * mvCorner).xyz);
}

View File

@@ -86,7 +86,8 @@ function checkActiveUniforms(gl: GLRenderingContext, program: WebGLProgram, sche
// name assigned by `gl.shim.ts`, ignore for checks
continue;
}
const spec = schema[name];
const baseName = name.replace(/[[0-9]+\]$/, ''); // 'array' uniforms
const spec = schema[baseName];
if (spec === undefined) {
throw new Error(`missing 'uniform' or 'texture' with name '${name}' in schema`);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -8,45 +8,33 @@ import { Mat3, Mat4, Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
import { ValueCell } from '../../mol-util';
import { GLRenderingContext } from './compat';
import { RenderableSchema } from '../../mol-gl/renderable/schema';
import { ValueOf } from '../../mol-util/type-helpers';
export type UniformKindValue = {
'f': number
'i': number
'v2': Vec2
'v3': Vec3
'v4': Vec4
'm3': Mat3
'm4': Mat4
't': number
'f': number; 'f[]': number[]
'i': number; 'i[]': number[]
'v2': Vec2; 'v2[]': number[]
'v3': Vec3; 'v3[]': number[]
'v4': Vec4; 'v4[]': number[]
'm3': Mat3; 'm3[]': number[]
'm4': Mat4; 'm4[]': number[]
't': number; 't[]': number[]
}
export type UniformKind = keyof UniformKindValue
export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4
export type UniformType = ValueOf<UniformKindValue>
export type UniformValues = { [k: string]: ValueCell<UniformType> }
export type UniformsList = [string, ValueCell<UniformType>][]
export function getUniformType(gl: GLRenderingContext, kind: UniformKind) {
switch (kind) {
case 'f': return gl.FLOAT;
case 'i': return gl.INT;
case 'v2': return gl.FLOAT_VEC2;
case 'v3': return gl.FLOAT_VEC3;
case 'v4': return gl.FLOAT_VEC4;
case 'm3': return gl.FLOAT_MAT3;
case 'm4': return gl.FLOAT_MAT4;
default: console.error(`unknown uniform kind '${kind}'`);
}
}
export function setUniform(gl: GLRenderingContext, location: WebGLUniformLocation | null, kind: UniformKind, value: any) {
switch (kind) {
case 'f': gl.uniform1f(location, value); break;
case 'i': case 't': gl.uniform1i(location, value); break;
case 'v2': gl.uniform2fv(location, value); break;
case 'v3': gl.uniform3fv(location, value); break;
case 'v4': gl.uniform4fv(location, value); break;
case 'm3': gl.uniformMatrix3fv(location, false, value); break;
case 'm4': gl.uniformMatrix4fv(location, false, value); break;
case 'f': case 'f[]': return gl.FLOAT;
case 'i': case 'i[]': return gl.INT;
case 'v2': case 'v2[]': return gl.FLOAT_VEC2;
case 'v3': case 'v3[]': return gl.FLOAT_VEC3;
case 'v4': case 'v4[]': return gl.FLOAT_VEC4;
case 'm3': case 'm3[]': return gl.FLOAT_MAT3;
case 'm4': case 'm4[]': return gl.FLOAT_MAT4;
default: console.error(`unknown uniform kind '${kind}'`);
}
}
@@ -55,24 +43,27 @@ export type UniformSetter = (gl: GLRenderingContext, location: number, value: an
export type UniformSetters = { [k: string]: UniformSetter }
function uniform1f (gl: GLRenderingContext, location: number, value: any) { gl.uniform1f(location, value); }
function uniform1fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform1fv(location, value); }
function uniform1i (gl: GLRenderingContext, location: number, value: any) { gl.uniform1i(location, value); }
function uniform1iv (gl: GLRenderingContext, location: number, value: any) { gl.uniform1iv(location, value); }
function uniform2fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform2fv(location, value); }
function uniform3fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform3fv(location, value); }
function uniform4fv (gl: GLRenderingContext, location: number, value: any) { gl.uniform4fv(location, value); }
function uniformMatrix3fv (gl: GLRenderingContext, location: number, value: any) { gl.uniformMatrix3fv(location, false, value); }
function uniformMatrix4fv (gl: GLRenderingContext, location: number, value: any) { gl.uniformMatrix4fv(location, false, value); }
function getUniformSetter(kind: UniformKind) {
function getUniformSetter(kind: UniformKind): UniformSetter {
switch (kind) {
case 'f': return uniform1f;
case 'f[]': return uniform1fv;
case 'i': case 't': return uniform1i;
case 'v2': return uniform2fv;
case 'v3': return uniform3fv;
case 'v4': return uniform4fv;
case 'm3': return uniformMatrix3fv;
case 'm4': return uniformMatrix4fv;
case 'i[]': case 't[]': return uniform1iv;
case 'v2': case 'v2[]': return uniform2fv;
case 'v3': case 'v3[]': return uniform3fv;
case 'v4': case 'v4[]': return uniform4fv;
case 'm3': case 'm3[]': return uniformMatrix3fv;
case 'm4': case 'm4[]': return uniformMatrix4fv;
}
throw new Error(`unknown uniform kind '${kind}'`);
}
export function getUniformSetters(schema: RenderableSchema) {

View File

@@ -20,6 +20,7 @@
import Mat4 from './mat4';
import { EPSILON } from '../3d';
import { NumberArray } from '../../../mol-util/type-helpers';
import { Sphere3D } from '../../geometry/primitives/sphere3d';
interface Vec4 extends Array<number> { [d: number]: number, '@type': 'vec4', length: 4 }
@@ -53,6 +54,18 @@ namespace Vec4 {
return out;
}
export function fromSphere(out: Vec4, sphere: Sphere3D) {
out[0] = sphere.center[0];
out[1] = sphere.center[1];
out[2] = sphere.center[2];
out[3] = sphere.radius;
return out;
}
export function ofSphere(sphere: Sphere3D) {
return fromSphere(zero(), sphere);
}
export function hasNaN(a: Vec4) {
return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]) || isNaN(a[3]);
}

View File

@@ -144,6 +144,27 @@ export namespace Model {
//
export function hasCarbohydrate(model: Model): boolean {
return model.properties.saccharideComponentMap.size > 0;
}
export function hasProtein(model: Model): boolean {
const { subtype } = model.entities;
for (let i = 0, il = subtype.rowCount; i < il; ++i) {
if (subtype.value(i).startsWith('polypeptide')) return true;
}
return false;
}
export function hasNucleic(model: Model): boolean {
const { subtype } = model.entities;
for (let i = 0, il = subtype.rowCount; i < il; ++i) {
const s = subtype.value(i);
if (s.endsWith('ribonucleotide hybrid') || s.endsWith('ribonucleotide')) return true;
}
return false;
}
export function isFromPdbArchive(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;

View File

@@ -88,24 +88,20 @@ export function getAtomicRanges(hierarchy: AtomicHierarchy, entities: Entities,
startIndex = residueSegment.start;
} else if (!residueIt.hasNext) {
polymerRanges.push(startIndex, residueSegment.end - 1);
// TODO
// if (seqId !== maxSeqId) {
// gapRanges.push(residueSegment.end - 1, residueSegment.end - 1)
// }
// TODO store terminal gaps
} else {
const riStart = hierarchy.residueAtomSegments.index[residueSegment.start];
const riEnd = hierarchy.residueAtomSegments.index[prevEnd - 1];
if (conformation.xyzDefined && !areBackboneConnected(riStart, riEnd, conformation, hierarchy.index, derived)) {
polymerRanges.push(startIndex, prevEnd - 1);
// add gap even for consecutive residues if they are not connected
gapRanges.push(prevStart, residueSegment.end - 1);
startIndex = residueSegment.start;
}
}
} else {
startIndex = residueSegment.start; // start polymer
// TODO
// if (seqId !== 1) {
// gapRanges.push(residueSegment.start, residueSegment.start)
// }
// TODO store terminal gaps
}
} else {
if (startIndex !== -1) {

View File

@@ -171,10 +171,10 @@ const Monosaccharides: SaccharideComponent[] = [
{ abbr: '4eLeg', name: '4-Epilegionaminic Acid', color: SaccharideColors.LightBlue, type: SaccharideType.DiDeoxynonulosonate },
{ abbr: 'Bac', name: 'Bacillosamine', color: SaccharideColors.Blue, type: SaccharideType.Unknown },
{ abbr: 'LDManHep', name: 'L-Glycero-D-Manno Heptose', color: SaccharideColors.Green, type: SaccharideType.Unknown },
{ abbr: 'LDmanHep', name: 'L-Glycero-D-Manno Heptose', color: SaccharideColors.Green, type: SaccharideType.Unknown },
{ abbr: 'Kdo', name: 'Keto-Deoxy Octulonic Acid', color: SaccharideColors.Yellow, type: SaccharideType.Unknown },
{ abbr: 'Dha', name: '3-Deoxy Lyxo-Heptulosaric Acid', color: SaccharideColors.Orange, type: SaccharideType.Unknown },
{ abbr: 'DDManHep', name: 'D-Glycero-D-Manno-Heptose', color: SaccharideColors.Pink, type: SaccharideType.Unknown },
{ abbr: 'DDmanHep', name: 'D-Glycero-D-Manno-Heptose', color: SaccharideColors.Pink, type: SaccharideType.Unknown },
{ abbr: 'MurNAc', name: 'N-Acetyl Muramic Acid', color: SaccharideColors.Purple, type: SaccharideType.Unknown },
{ abbr: 'MurNGc', name: 'N-Glycolyl Muramic Acid', color: SaccharideColors.LightBlue, type: SaccharideType.Unknown },
{ abbr: 'Mur', name: 'Muramic Acid', color: SaccharideColors.Brown, type: SaccharideType.Unknown },
@@ -220,7 +220,7 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
Gul: ['4GL', 'GL0', 'GUP', 'Z8H'],
Alt: ['Z6H', '3MK', 'SHD'],
All: ['AFD', 'ALL', 'WOO', 'Z2D'],
Tal: ['ZEE', 'A5C'],
Tal: ['ZEE', 'A5C', 'SDY'],
Ido: ['ZCD', 'Z0F', '4N2'],
// HexNAc
GlcNAc: ['NDG', 'NAG', 'NGZ'],
@@ -230,7 +230,7 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
AltNAc: [],
AllNAc: ['NAA'],
TalNAc: [],
IdoNAc: ['LXZ'],
IdoNAc: ['LXZ', 'HSQ'],
// Hexosamine
GlcN: ['PA1', 'GCS'],
ManN: ['95Z'],
@@ -255,7 +255,7 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
'6dGul': ['66O'],
'6dAlt': [],
'6dTal': [],
Fuc: ['FUC', 'FUL', 'FCA', 'FCB'],
Fuc: ['FUC', 'FUL', 'FCA', 'FCB', 'GYE'],
// DeoxyhexNAc
QuiNAc: ['Z9W'],
RhaNAc: [],
@@ -270,7 +270,7 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
Dig: ['Z3U'],
Col: [],
// Pentose
Ara: ['64K', 'ARA', 'ARB', 'AHR', 'FUB', 'BXY', 'BXX'],
Ara: ['64K', 'ARA', 'ARB', 'AHR', 'FUB', 'BXY', 'BXX', 'SEJ'],
Lyx: ['LDY', 'Z4W'],
Xyl: ['XYS', 'XYP', 'XYZ', 'HSY', 'LXC'],
Rib: ['YYM', 'RIP', 'RIB', 'BDR', '0MK', 'Z6J', '32O'],
@@ -287,10 +287,10 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
'4eLeg': [],
// Unknown
Bac: [],
LDManHep: ['GMH'],
LDmanHep: ['GMH'],
Kdo: ['KDO'],
Dha: [],
DDManHep: ['289'],
DDmanHep: ['289'],
MurNAc: ['MUB', 'AMU'],
MurNGc: [],
Mur: ['1S4', 'MUR'],
@@ -298,8 +298,8 @@ const CommonSaccharideNames: { [k: string]: string[] } = {
Api: ['XXM'],
Fru: ['BDF', 'Z9N', 'FRU', 'LFR'],
Tag: ['T6T'],
Sor: ['SOE'],
Psi: ['PSV', 'SF6', 'SF9'],
Sor: ['SOE', 'UEA'],
Psi: ['PSV', 'SF6', 'SF9', 'TTV'],
};
const UnknownSaccharideNames = [

View File

@@ -61,10 +61,6 @@ const DownloadStructure = StateAction.build({
}, { pivot: 'id' }),
options
}, { isFlat: true, label: 'PDBDEV' }),
'bcif-static': PD.Group({
id: PD.Text('1tqn', { label: 'PDB Id(s)', description: 'One or more comma/space separated PDB ids.' }),
options
}, { isFlat: true, label: 'BinaryCIF (static PDBe Updated)' }),
'swissmodel': PD.Group({
id: PD.Text('Q9Y2I8', { label: 'UniProtKB AC(s)', description: 'One or more comma/space separated ACs.' }),
options
@@ -119,10 +115,6 @@ const DownloadStructure = StateAction.build({
);
asTrajectory = !!src.params.options.asTrajectory;
break;
case 'bcif-static':
downloadParams = getDownloadParams(src.params.id, id => `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${id.toLowerCase()}`, id => `BinaryCIF: ${id}`, true);
asTrajectory = !!src.params.options.asTrajectory;
break;
case 'swissmodel':
downloadParams = getDownloadParams(src.params.id, id => `https://swissmodel.expasy.org/repository/uniprot/${id.toUpperCase()}.pdb`, id => `SWISS-MODEL: ${id}`, false);
asTrajectory = !!src.params.options.asTrajectory;

View File

@@ -90,7 +90,7 @@ export class StructureBuilder {
const model = StateObjectRef.resolveAndCheck(state, modelRef);
if (model) {
const symm = ModelSymmetry.Provider.get(model.obj?.data!);
if (!symm || symm?.assemblies.length === 0) params = { name: 'deposited', params: { } };
if (!symm || symm?.assemblies.length === 0) params = { name: 'model', params: { } };
}
}

View File

@@ -43,7 +43,7 @@ const defaultPreset = TrajectoryHierarchyPresetProvider({
id: 'preset-trajectory-default',
display: {
name: 'Default (Assembly)', group: 'Preset',
description: 'Shows the first assembly or, if that is unavailable, the first deposited model.'
description: 'Shows the first assembly or, if that is unavailable, the first model.'
},
isApplicable: o => {
return true;
@@ -93,7 +93,7 @@ const allModels = TrajectoryHierarchyPresetProvider({
for (let i = 0; i < tr.length; i++) {
const model = await builder.createModel(trajectory, { modelIndex: i });
const modelProperties = await builder.insertModelProperties(model, params.modelProperties, { isCollapsed: true });
const structure = await builder.createStructure(modelProperties || model, { name: 'deposited', params: {} });
const structure = await builder.createStructure(modelProperties || model, { name: 'model', params: {} });
const structureProperties = await builder.insertStructureProperties(structure, params.structureProperties);
models.push(model);

View File

@@ -17,7 +17,7 @@ import { PluginStateObject as SO } from '../objects';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
export namespace RootStructureDefinition {
export function getParams(model?: Model, defaultValue?: 'auto' | 'deposited' | 'assembly' | 'symmetry' | 'symmetry-mates' | 'symmetry-assembly') {
export function getParams(model?: Model, defaultValue?: 'auto' | 'model' | 'assembly' | 'symmetry' | 'symmetry-mates' | 'symmetry-assembly') {
const symmetry = model && ModelSymmetry.Provider.get(model);
const assemblyIds = symmetry ? symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]) : [];
@@ -41,7 +41,7 @@ export namespace RootStructureDefinition {
const modes = {
auto: PD.EmptyGroup(),
deposited: PD.EmptyGroup(),
model: PD.EmptyGroup(),
assembly: PD.Group({
id: PD.Optional(model
? PD.Select(assemblyIds.length ? assemblyIds[0][0] : '', assemblyIds, { label: 'Asm Id', description: 'Assembly Id' })
@@ -75,7 +75,7 @@ export namespace RootStructureDefinition {
options.push(['auto', 'Auto']);
}
options.push(['deposited', 'Deposited']);
options.push(['model', 'Model']);
if (assemblyIds.length > 0) {
options.push(['assembly', 'Assembly']);
@@ -88,7 +88,7 @@ export namespace RootStructureDefinition {
}
return {
type: PD.MappedStatic(defaultValue || 'deposited', modes, { options })
type: PD.MappedStatic(defaultValue || 'model', modes, { options })
};
}
@@ -110,19 +110,17 @@ export namespace RootStructureDefinition {
}
if (!symmetry || symmetry.assemblies.length === 0) {
if (id !== 'deposited') {
plugin.log.warn(`Model '${model.entryId}' has no assembly, returning deposited structure.`);
}
plugin.log.warn(`Model '${model.entryId}' has no assembly, returning model structure.`);
} else {
asm = Symmetry.findAssembly(model, id || '');
if (!asm) {
plugin.log.warn(`Model '${model.entryId}' has no assembly called '${id}', returning deposited structure.`);
plugin.log.warn(`Model '${model.entryId}' has no assembly called '${id}', returning model structure.`);
}
}
const base = Structure.ofModel(model);
if (!asm) {
const label = { label: 'Deposited', description: Structure.elementDescription(base) };
const label = { label: 'Model', description: Structure.elementDescription(base) };
return new SO.Molecule.Structure(base, label);
}
@@ -155,14 +153,14 @@ export namespace RootStructureDefinition {
export async function create(plugin: PluginContext, ctx: RuntimeContext, model: Model, params?: Params): Promise<SO.Molecule.Structure> {
const symmetry = ModelSymmetry.Provider.get(model);
if (!symmetry || !params || params.name === 'deposited') {
if (!symmetry || !params || params.name === 'model') {
const s = Structure.ofModel(model);
return new SO.Molecule.Structure(s, { label: 'Deposited', description: Structure.elementDescription(s) });
return new SO.Molecule.Structure(s, { label: 'Model', description: Structure.elementDescription(s) });
}
if (params.name === 'auto') {
if (symmetry.assemblies.length === 0) {
const s = Structure.ofModel(model);
return new SO.Molecule.Structure(s, { label: 'Deposited', description: Structure.elementDescription(s) });
return new SO.Molecule.Structure(s, { label: 'Model', description: Structure.elementDescription(s) });
} else {
return buildAssembly(plugin, ctx, model);
}

View File

@@ -0,0 +1,65 @@
/**
* 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>
*/
import { Structure, StructureElement } from '../../mol-model/structure';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { PluginContext } from '../../mol-plugin/context';
import { StateBuilder, StateObjectCell, StateSelection, StateTransform } from '../../mol-state';
import { StructureComponentRef } from '../manager/structure/hierarchy-state';
import { EmptyLoci, Loci } from '../../mol-model/loci';
import { Clipping } from '../../mol-theme/clipping';
type ClippingEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, clipping?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.ClippingStructureRepresentation3DFromBundle>>) => void
const ClippingManagerTag = 'clipping-controls';
export async function setStructureClipping(plugin: PluginContext, components: StructureComponentRef[], groups: Clipping.Groups, lociGetter: (structure: Structure) => StructureElement.Loci | EmptyLoci, types?: string[]) {
await eachRepr(plugin, components, (update, repr, clippingCell) => {
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
const structure = repr.obj!.data.source.data;
// always use the root structure to get the loci so the clipping
// stays applicable as long as the root structure does not change
const loci = lociGetter(structure.root);
if (Loci.isEmpty(loci)) return;
const layer = {
bundle: StructureElement.Bundle.fromLoci(loci),
groups
};
if (clippingCell) {
const bundleLayers = [...clippingCell.params!.values.layers, layer];
const filtered = getFilteredBundle(bundleLayers, structure);
update.to(clippingCell).update(Clipping.toBundle(filtered));
} else {
const filtered = getFilteredBundle([layer], structure);
update.to(repr.transform.ref)
.apply(StateTransforms.Representation.ClippingStructureRepresentation3DFromBundle, Clipping.toBundle(filtered), { tags: ClippingManagerTag });
}
});
}
function eachRepr(plugin: PluginContext, components: StructureComponentRef[], callback: ClippingEachReprCallback) {
const state = plugin.state.data;
const update = state.build();
for (const c of components) {
for (const r of c.representations) {
const clipping = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.ClippingStructureRepresentation3DFromBundle, r.cell.transform.ref).withTag(ClippingManagerTag));
callback(update, r.cell, clipping[0]);
}
}
return update.commit({ doNotUpdateCurrent: true });
}
/** filter clipping layers for given structure */
function getFilteredBundle(layers: Clipping.BundleLayer[], structure: Structure) {
const clipping = Clipping.ofBundle(layers, structure.root);
const merged = Clipping.merge(clipping);
return Clipping.filter(merged, structure);
}

View File

@@ -26,6 +26,8 @@ import { createStructureColorThemeParams, createStructureSizeThemeParams } from
import { StructureSelectionQueries, StructureSelectionQuery } from '../../helpers/structure-selection-query';
import { StructureRepresentation3D } from '../../transforms/representation';
import { StructureHierarchyRef, StructureComponentRef, StructureRef, StructureRepresentationRef } from './hierarchy-state';
import { Clipping } from '../../../mol-theme/clipping';
import { setStructureClipping } from '../../helpers/structure-clipping';
export { StructureComponentManager };
@@ -358,7 +360,7 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
}, { canUndo: 'Add Selection' });
}
async applyColor(params: StructureComponentManager.ColorParams, structures?: ReadonlyArray<StructureRef>) {
async applyTheme(params: StructureComponentManager.ThemeParams, structures?: ReadonlyArray<StructureRef>) {
return this.plugin.dataTransaction(async () => {
const xs = structures || this.currentStructures;
if (xs.length === 0) return;
@@ -367,12 +369,15 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
for (const s of xs) {
if (params.action.name === 'reset') {
await clearStructureOverpaint(this.plugin, s.components, params.representations);
} else {
} else if (params.action.name === 'color') {
const p = params.action.params;
await setStructureOverpaint(this.plugin, s.components, p.color, getLoci, params.representations, p.opacity);
} else if (params.action.name === 'clipping') {
const p = params.action.params;
await setStructureClipping(this.plugin, s.components, Clipping.Groups.fromNames(p.excludeGroups), getLoci, params.representations);
}
}
}, { canUndo: 'Apply Color' });
}, { canUndo: 'Apply Theme' });
}
private modifyComponent(builder: StateBuilder.Root, component: StructureComponentRef, by: Structure, action: StructureComponentManager.ModifyAction) {
@@ -448,19 +453,22 @@ namespace StructureComponentManager {
}
export type AddParams = { selection: StructureSelectionQuery, options: { checkExisting: boolean, label: string }, representation: string }
export function getColorParams(plugin: PluginContext, pivot: StructureRef | StructureComponentRef | undefined) {
export function getThemeParams(plugin: PluginContext, pivot: StructureRef | StructureComponentRef | undefined) {
return {
action: PD.MappedStatic('color', {
color: PD.Group({
color: PD.Color(ColorNames.blue, { isExpanded: true }),
opacity: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
}, { isFlat: true }),
reset: PD.EmptyGroup()
reset: PD.EmptyGroup(),
clipping: PD.Group({
excludeGroups: PD.MultiSelect([] as Clipping.Groups.Names[], PD.objectToOptions(Clipping.Groups.Names)),
}, { isFlat: true }),
}),
representations: PD.MultiSelect([], getRepresentationTypes(plugin, pivot), { emptyValue: 'All' })
};
}
export type ColorParams = PD.Values<ReturnType<typeof getColorParams>>
export type ThemeParams = PD.Values<ReturnType<typeof getThemeParams>>
export function getRepresentationTypes(plugin: PluginContext, pivot: StructureRef | StructureComponentRef | undefined) {
return pivot?.cell.obj?.data

View File

@@ -371,7 +371,7 @@ const StructureFromTrajectory = PluginStateTransform.BuiltIn({
type StructureFromModel = typeof StructureFromModel
const StructureFromModel = PluginStateTransform.BuiltIn({
name: 'structure-from-model',
display: { name: 'Structure', description: 'Create a molecular structure (deposited, assembly, or symmetry) from the specified model.' },
display: { name: 'Structure', description: 'Create a molecular structure (model, assembly, or symmetry) from the specified model.' },
from: SO.Molecule.Model,
to: SO.Molecule.Structure,
params(a) { return RootStructureDefinition.getParams(a && a.data); }

View File

@@ -34,6 +34,8 @@ import { OrientationRepresentation, OrientationParams } from '../../mol-repr/sha
import { AngleParams, AngleRepresentation } from '../../mol-repr/shape/loci/angle';
import { DihedralParams, DihedralRepresentation } from '../../mol-repr/shape/loci/dihedral';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
import { Clipping } from '../../mol-theme/clipping';
import { ObjectKeys } from '../../mol-util/type-helpers';
export { StructureRepresentation3D };
export { ExplodeStructureRepresentation3D };
@@ -42,6 +44,8 @@ export { OverpaintStructureRepresentation3DFromScript };
export { OverpaintStructureRepresentation3DFromBundle };
export { TransparencyStructureRepresentation3DFromScript };
export { TransparencyStructureRepresentation3DFromBundle };
export { ClippingStructureRepresentation3DFromScript };
export { ClippingStructureRepresentation3DFromBundle };
export { VolumeRepresentation3D };
type StructureRepresentation3D = typeof StructureRepresentation3D
@@ -438,6 +442,99 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
}
});
type ClippingStructureRepresentation3DFromScript = typeof ClippingStructureRepresentation3DFromScript
const ClippingStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
name: 'clipping-structure-representation-3d-from-script',
display: 'Clipping 3D Representation',
from: SO.Molecule.Structure.Representation3D,
to: SO.Molecule.Structure.Representation3DState,
params: {
layers: PD.ObjectList({
script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
groups: PD.Converted((g: Clipping.Groups) => Clipping.Groups.toNames(g), n => Clipping.Groups.fromNames(n), PD.MultiSelect(ObjectKeys(Clipping.Groups.Names), PD.objectToOptions(Clipping.Groups.Names))),
}, e => `${Clipping.Groups.toNames(e.groups).length} group(s)`, {
defaultValue: [{
script: Script('(sel.atom.all)', 'mol-script'),
groups: Clipping.Groups.Flag.None,
}]
}),
}
})({
canAutoUpdate() {
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const clipping = Clipping.ofScript(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { clipping },
initialState: { clipping: Clipping.Empty },
info: structure,
source: a
}, { label: `Clipping (${clipping.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const structure = b.data.info as Structure;
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
const oldClipping = b.data.state.clipping!;
const newClipping = Clipping.ofScript(newParams.layers, structure);
if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.clipping = newClipping;
b.data.source = a;
b.label = `Clipping (${newClipping.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
});
type ClippingStructureRepresentation3DFromBundle = typeof ClippingStructureRepresentation3DFromBundle
const ClippingStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
name: 'clipping-structure-representation-3d-from-bundle',
display: 'Clipping 3D Representation',
from: SO.Molecule.Structure.Representation3D,
to: SO.Molecule.Structure.Representation3DState,
params: {
layers: PD.ObjectList({
bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
groups: PD.Converted((g: Clipping.Groups) => Clipping.Groups.toNames(g), n => Clipping.Groups.fromNames(n), PD.MultiSelect(ObjectKeys(Clipping.Groups.Names), PD.objectToOptions(Clipping.Groups.Names))),
}, e => `${Clipping.Groups.toNames(e.groups).length} group(s)`, {
defaultValue: [{
bundle: StructureElement.Bundle.Empty,
groups: Clipping.Groups.Flag.None,
}],
isHidden: true
}),
}
})({
canAutoUpdate() {
return true;
},
apply({ a, params }) {
const structure = a.data.source.data;
const clipping = Clipping.ofBundle(params.layers, structure);
return new SO.Molecule.Structure.Representation3DState({
state: { clipping },
initialState: { clipping: Clipping.Empty },
info: structure,
source: a
}, { label: `Clipping (${clipping.layers.length} Layers)` });
},
update({ a, b, newParams, oldParams }) {
const structure = b.data.info as Structure;
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
const oldClipping = b.data.state.clipping!;
const newClipping = Clipping.ofBundle(newParams.layers, structure);
if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
b.data.state.clipping = newClipping;
b.data.source = a;
b.label = `Clipping (${newClipping.layers.length} Layers)`;
return StateTransformer.UpdateResult.Updated;
}
});
//
export namespace VolumeRepresentation3DHelpers {

View File

@@ -9,9 +9,7 @@ import * as React from 'react';
import { Observable, Subscription } from 'rxjs';
import { PluginContext } from '../mol-plugin/context';
import { Button, ColorAccent } from './controls/common';
import ArrowRight from '@material-ui/icons/ArrowRight';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import { Icon } from './controls/icons';
import { Icon, ArrowRightSvg, ArrowDropDownSvg } from './controls/icons';
export const PluginReactContext = React.createContext(void 0 as any as PluginContext);
@@ -104,7 +102,7 @@ export abstract class CollapsableControls<P = {}, S = {}, SS = {}> extends Plugi
return <div className={wrapClass}>
<div className='msp-transform-header'>
<Button icon={this.state.brand ? void 0 : this.state.isCollapsed ? ArrowRight : ArrowDropDown} noOverflow onClick={this.toggleCollapsed}
<Button icon={this.state.brand ? void 0 : this.state.isCollapsed ? ArrowRightSvg : ArrowDropDownSvg} noOverflow onClick={this.toggleCollapsed}
className={this.state.brand ? `msp-transform-header-brand msp-transform-header-brand-${this.state.brand.accent}` : void 0} title={`Click to ${this.state.isCollapsed ? 'expand' : 'collapse'}`}>
{/* {this.state.brand && <div className={`msp-accent-bg-${this.state.brand.accent}`}>{this.state.brand.svg ? <Icon svg={this.state.brand.svg} /> : this.state.brand.name}</div>} */}
<Icon svg={this.state.brand?.svg} inline />

View File

@@ -5,13 +5,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import Build from '@material-ui/icons/Build';
import NavigateBefore from '@material-ui/icons/NavigateBefore';
import NavigateNext from '@material-ui/icons/NavigateNext';
import PlayArrow from '@material-ui/icons/PlayArrow';
import SkipPrevious from '@material-ui/icons/SkipPrevious';
import Stop from '@material-ui/icons/Stop';
import SubscriptionsOutlined from '@material-ui/icons/SubscriptionsOutlined';
import * as React from 'react';
import { UpdateTrajectory } from '../mol-plugin-state/actions/structure';
import { LociLabel } from '../mol-plugin-state/manager/loci-label';
@@ -22,7 +15,7 @@ import { PluginCommands } from '../mol-plugin/commands';
import { StateTransformer } from '../mol-state';
import { PluginUIComponent } from './base';
import { IconButton } from './controls/common';
import { Icon } from './controls/icons';
import { Icon, NavigateBeforeSvg, NavigateNextSvg, SkipPreviousSvg, StopSvg, PlayArrowSvg, SubscriptionsOutlinedSvg, BuildSvg } from './controls/icons';
import { AnimationControls } from './state/animation';
import { StructureComponentControls } from './structure/components';
import { StructureMeasurementsControls } from './structure/measurements';
@@ -97,9 +90,9 @@ export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: bo
if (!this.state.show || (isAnimating && !this.state.label)) return null;
return <div className='msp-traj-controls'>
{!isAnimating && <IconButton svg={SkipPrevious} title='First Model' onClick={this.reset} disabled={isAnimating} />}
{!isAnimating && <IconButton svg={NavigateBefore} title='Previous Model' onClick={this.prev} disabled={isAnimating} />}
{!isAnimating && <IconButton svg={NavigateNext} title='Next Model' onClick={this.next} disabled={isAnimating} />}
{!isAnimating && <IconButton svg={SkipPreviousSvg} title='First Model' onClick={this.reset} disabled={isAnimating} />}
{!isAnimating && <IconButton svg={NavigateBeforeSvg} title='Previous Model' onClick={this.prev} disabled={isAnimating} />}
{!isAnimating && <IconButton svg={NavigateNextSvg} title='Next Model' onClick={this.next} disabled={isAnimating} />}
{!!this.state.label && <span>{this.state.label}</span> }
</div>;
}
@@ -187,11 +180,11 @@ export class StateSnapshotViewportControls extends PluginUIComponent<{}, { isBus
{!current && <option key='none' value='none'></option>}
{snapshots.state.entries.valueSeq().map((e, i) => <option key={e!.snapshot.id} value={e!.snapshot.id}>{`[${i! + 1}/${count}]`} {e!.name || new Date(e!.timestamp).toLocaleString()}</option>)}
</select>
<IconButton svg={isPlaying ? Stop : PlayArrow} title={isPlaying ? 'Pause' : 'Cycle States'} onClick={this.togglePlay}
<IconButton svg={isPlaying ? StopSvg : PlayArrowSvg} title={isPlaying ? 'Pause' : 'Cycle States'} onClick={this.togglePlay}
disabled={isPlaying ? false : this.state.isBusy} />
{!isPlaying && <>
<IconButton svg={NavigateBefore} title='Previous State' onClick={this.prev} disabled={this.state.isBusy || isPlaying} />
<IconButton svg={NavigateNext} title='Next State' onClick={this.next} disabled={this.state.isBusy || isPlaying} />
<IconButton svg={NavigateBeforeSvg} title='Previous State' onClick={this.prev} disabled={this.state.isBusy || isPlaying} />
<IconButton svg={NavigateNextSvg} title='Next State' onClick={this.next} disabled={this.state.isBusy || isPlaying} />
</>}
</div>;
}
@@ -229,7 +222,7 @@ export class AnimationViewportControls extends PluginUIComponent<{}, { isEmpty:
return <div className='msp-animation-viewport-controls'>
<div>
<div className='msp-semi-transparent-background' />
<IconButton svg={isAnimating || isPlaying ? Stop : SubscriptionsOutlined} transparent title={isAnimating ? 'Stop' : 'Select Animation'}
<IconButton svg={isAnimating || isPlaying ? StopSvg : SubscriptionsOutlinedSvg} transparent title={isAnimating ? 'Stop' : 'Select Animation'}
onClick={isAnimating || isPlaying ? this.stop : this.toggleExpanded} toggleState={this.state.isExpanded}
disabled={isAnimating || isPlaying ? false : this.state.isBusy || this.state.isPlaying || this.state.isEmpty} />
</div>
@@ -293,7 +286,7 @@ export class CustomStructureControls extends PluginUIComponent<{ initiallyCollap
export class DefaultStructureTools extends PluginUIComponent {
render() {
return <>
<div className='msp-section-header'><Icon svg={Build} />Structure Tools</div>
<div className='msp-section-header'><Icon svg={BuildSvg} />Structure Tools</div>
<StructureSourceControls />
<StructureMeasurementsControls />

View File

@@ -8,10 +8,7 @@
import * as React from 'react';
import { ParamDefinition } from '../../mol-util/param-definition';
import { Button, ControlGroup } from './common';
import ArrowRight from '@material-ui/icons/ArrowRight';
import Check from '@material-ui/icons/Check';
import Close from '@material-ui/icons/Close';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import { CloseSvg, ArrowDropDownSvg, ArrowRightSvg, CheckSvg } from './icons';
export class ActionMenu extends React.PureComponent<ActionMenu.Props> {
hide = () => this.props.onSelect(void 0)
@@ -20,7 +17,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} title={cmd.title} 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={CloseSvg}>
{section}
</ControlGroup>}
{!cmd.header && section}
@@ -229,13 +226,13 @@ class Section extends React.PureComponent<SectionProps, SectionState> {
const { header, hasCurrent } = this.state;
return <div className='msp-flex-row msp-control-group-header'>
<Button icon={this.state.isExpanded ? ArrowDropDown : ArrowRight} flex noOverflow onClick={this.toggleExpanded} title={`Click to ${this.state.isExpanded ? 'collapse' : 'expand'}.${header?.description ? ` ${header?.description}` : ''}`}>
<Button icon={this.state.isExpanded ? ArrowDropDownSvg : ArrowRightSvg} flex noOverflow onClick={this.toggleExpanded} title={`Click to ${this.state.isExpanded ? 'collapse' : 'expand'}.${header?.description ? ` ${header?.description}` : ''}`}>
{hasCurrent ? <b>{header?.label}</b> : header?.label}
</Button>
<Button icon={Check} flex onClick={this.selectAll} style={{ flex: '0 0 50px', textAlign: 'right' }}>
<Button icon={CheckSvg} flex onClick={this.selectAll} style={{ flex: '0 0 50px', textAlign: 'right' }}>
All
</Button>
<Button icon={Close} flex onClick={this.selectNone} style={{ flex: '0 0 50px', textAlign: 'right' }}>
<Button icon={CloseSvg} flex onClick={this.selectNone} style={{ flex: '0 0 50px', textAlign: 'right' }}>
None
</Button>
</div>;
@@ -245,7 +242,7 @@ class Section extends React.PureComponent<SectionProps, SectionState> {
const { header, hasCurrent } = this.state;
return <div className='msp-control-group-header' style={{ marginTop: '1px' }}>
<Button noOverflow icon={this.state.isExpanded ? ArrowDropDown : ArrowRight} onClick={this.toggleExpanded} title={`Click to ${this.state.isExpanded ? 'collapse' : 'expand'}. ${header?.description ? header?.description : ''}`}>
<Button noOverflow icon={this.state.isExpanded ? ArrowDropDownSvg : ArrowRightSvg} onClick={this.toggleExpanded} title={`Click to ${this.state.isExpanded ? 'collapse' : 'expand'}. ${header?.description ? header?.description : ''}`}>
{hasCurrent ? <b>{header?.label}</b> : header?.label}
</Button>
</div>;

View File

@@ -6,11 +6,7 @@
import * as React from 'react';
import { Color } from '../../mol-util/color';
import { Icon } from './icons';
import ArrowRight from '@material-ui/icons/ArrowRight';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import Remove from '@material-ui/icons/Remove';
import Add from '@material-ui/icons/Add';
import { Icon, ArrowRightSvg, ArrowDropDownSvg, RemoveSvg, AddSvg } from './icons';
export type ColorAccent = 'cyan' | 'red' | 'gray' | 'green' | 'purple' | 'blue' | 'orange'
@@ -45,7 +41,7 @@ export class ControlGroup extends React.Component<{
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 }} title={this.props.title}>
<Button onClick={this.headerClicked}>
{!this.props.hideExpander && <Icon svg={this.state.isExpanded ? ArrowRight : ArrowDropDown} />}
{!this.props.hideExpander && <Icon svg={this.state.isExpanded ? ArrowRightSvg : ArrowDropDownSvg} />}
{this.props.topRightIcon && <Icon svg={this.props.topRightIcon} style={{ position: 'absolute', right: '2px', top: 0 }} />}
<b>{this.props.header}</b>
</Button>
@@ -206,7 +202,7 @@ export class ExpandableControlRow extends React.Component<{
{label}
<button className='msp-btn-link msp-btn-icon msp-control-group-expander' onClick={this.toggleExpanded} title={`${this.state.isExpanded ? 'Less' : 'More'} options`}
style={{ background: 'transparent', textAlign: 'left', padding: '0' }}>
<Icon svg={this.state.isExpanded ? Remove : Add} style={{ display: 'inline-block' }} />
<Icon svg={this.state.isExpanded ? RemoveSvg : AddSvg} style={{ display: 'inline-block' }} />
</button>
</>} control={pivot}>
{this.props.colorStripe && <div className='msp-expandable-group-color-stripe' style={{ backgroundColor: Color.toStyle(this.props.colorStripe) }} />}
@@ -347,7 +343,7 @@ export class ExpandGroup extends React.PureComponent<{ header: string, headerSty
return <>
<div className='msp-control-group-header' style={{ marginTop: this.props.marginTop !== void 0 ? this.props.marginTop : '1px', marginLeft: this.props.headerLeftMargin }}>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleExpanded} style={this.props.headerStyle}>
<Icon svg={this.state.isExpanded ? ArrowDropDown : ArrowRight} />
<Icon svg={this.state.isExpanded ? ArrowDropDownSvg : ArrowRightSvg} />
{this.props.header}
</button>
</div>

View File

@@ -21,92 +21,144 @@ export function Icon(props: {
//
export function UnionSvg() { return _union; }
export function SubtractSvg() { return _subtract; }
export function IntersectSvg() { return _intersect; }
export function SetSvg() { return _set; }
export function MoleculeSvg() { return _molecule; }
export function RulerSvg() { return _ruler; }
export function CubeSvg() { return _cube; }
export function CursorSvg() { return _cursor; }
const circleLeft = <circle r='6px' id='circle-left' cy='12px' cx='8px' strokeWidth='1px' />;
const circleRight = <circle r='6px' id='circle-right' cy='12px' cx='16px' strokeWidth='1px' />;
const circleLeft = <circle r="6px" id="circle-left" cy="12px" cx="8px" strokeWidth="1" />;
const circleRight = <circle r="6px" id="circle-right" cy="12px" cx="16px" strokeWidth="1" />;
const _Union = <svg width='24px' height='24px' viewBox='0 0 24 24'><defs>{circleLeft}{circleRight}</defs><g><use href='#circle-left' className='msp-shape-filled' /><use href='#circle-right' className='msp-shape-filled' /><use href='#circle-left' className='msp-shape-empty' /></g></svg>;
export function UnionSvg() { return _Union; }
const _Subtract = <svg width='24px' height='24px' viewBox='0 0 24 24'><defs>{circleLeft}{circleRight}<mask id='mask-left'><use href='#circle-left' fill='white' stroke='white' /><use href='#circle-right' fill='black' strokeWidth='0px' stroke='white' /></mask></defs><g><use href='#circle-left' className='msp-shape-filled' mask='url(#mask-left)' /><use href='#circle-right' className='msp-shape-empty' /></g></svg>;
export function SubtractSvg() { return _Subtract; }
const _Intersect = <svg width='24px' height='24px' viewBox='0 0 24 24'><defs>{circleLeft}{circleRight}<clipPath id='clip-left'><use href='#circle-right' /></clipPath></defs><g><use href='#circle-left' className='msp-shape-filled' clipPath='url(#clip-left)' /><use href='#circle-left' className='msp-shape-empty' /><use href='#circle-right' className='msp-shape-empty' /></g></svg>;
export function IntersectSvg() { return _Intersect; }
const _Set = <svg width='24px' height='24px' viewBox='0 0 24 24'><defs>{circleLeft}{circleRight}</defs><g><use href='#circle-left' className='msp-shape-empty' /><use href='#circle-right' className='msp-shape-filled' /></g></svg>;
export function SetSvg() { return _Set; }
const _Molecule = <svg width='17px' height='17px' viewBox='0 0 299.463 299.463' strokeWidth='6px'><g><path d='M256.851,173.832v-48.201c22.916-4.918,34.151-30.668,22.556-50.771c-11.547-20.004-39.486-23.251-55.242-5.844 l-41.746-24.106C189.618,22.603,172.861,0,149.734,0c-23.132,0-39.881,22.609-32.685,44.911L75.305,69.016 C59.522,51.586,31.597,54.88,20.061,74.863c-11.63,20.163-0.298,45.862,22.557,50.769v48.2 c-22.821,4.898-34.195,30.591-22.556,50.771c11.529,19.972,39.454,23.285,55.242,5.845l41.746,24.106 c-7.199,22.308,9.559,44.911,32.685,44.911c23.132,0,39.88-22.609,32.685-44.911l41.745-24.106 c15.817,17.469,43.73,14.099,55.242-5.844c0,0,0-0.001,0.001-0.002c4.587-7.953,5.805-17.213,3.431-26.076 C279.392,185.657,269.129,176.461,256.851,173.832z M249.62,72.088c20.568,0,27.428,27.191,10.008,37.239 c-0.003,0.002-0.006,0.003-0.009,0.005c-10.04,5.81-22.85,1.762-27.877-8.475C225.206,87.548,234.938,72.088,249.62,72.088z M149.734,14.4c11.005,0,19.958,8.954,19.958,19.959c0,11.127-9.077,19.958-19.958,19.958c-10.95,0-19.958-8.9-19.958-19.958 C129.776,23.354,138.729,14.4,149.734,14.4z M39.84,109.328c-17.451-10.067-10.534-37.24,10.01-37.24 c15.311,0,24.922,16.653,17.251,29.942C61.681,111.397,49.517,114.925,39.84,109.328z M59.802,224.702 c-9.535,5.503-21.768,2.229-27.268-7.298c-7.639-13.242,1.887-29.945,17.236-29.945c0.013,0,0.027,0,0.04,0 C70.07,187.48,77.49,214.469,59.802,224.702z M149.734,285.062c-11.005,0-19.958-8.954-19.958-19.958 c0-11.127,9.077-19.958,19.958-19.958c10.954,0,19.958,8.903,19.958,19.958C169.693,276.109,160.74,285.062,149.734,285.062z M216.953,217.982l-41.727,24.095c-13.778-15.22-37.459-14.94-50.983,0l-41.728-24.096c6.196-19.289-5.541-39.835-25.498-44.149 V125.63c19.752-4.268,31.762-24.65,25.498-44.149l41.727-24.095c13.629,15.055,37.32,15.093,50.983,0l41.728,24.096 c-6.196,19.29,5.534,39.835,25.498,44.149v48.202C222.61,178.123,210.721,198.581,216.953,217.982z M266.935,217.404 c-5.501,9.528-17.732,12.802-27.261,7.302c-17.682-10.23-10.301-37.247,10.032-37.247 C264.984,187.459,274.602,204.112,266.935,217.404z'/></g></svg>;
export function MoleculeSvg() { return _Molecule; }
const _union = <svg width="24px" height="24px" viewBox="0 0 24 24">
<defs>
{circleLeft}
{circleRight}
</defs>
<g>
<use href="#circle-left" className="msp-shape-filled" />
<use href="#circle-right" className="msp-shape-filled" />
<use href="#circle-left" className="msp-shape-empty" />
</g>
</svg>;
const _subtract = <svg width="24px" height="24px" viewBox="0 0 24 24">
<defs>
{circleLeft}
{circleRight}
<mask id="mask-left">
<use href="#circle-left" fill="white" stroke="white" />
<use href="#circle-right" fill="black" strokeWidth="0" stroke="white" />
</mask>
</defs>
<g>
<use href="#circle-left" className="msp-shape-filled" mask="url(#mask-left)" />
<use href="#circle-right" className="msp-shape-empty" />
</g>
</svg>;
const _intersect = <svg width="24px" height="24px" viewBox="0 0 24 24">
<defs>
{circleLeft}
{circleRight}
<clipPath id="clip-left">
<use href="#circle-right" />
</clipPath>
</defs>
<g>
<use href="#circle-left" className="msp-shape-filled" clipPath="url(#clip-left)" />
<use href="#circle-left" className="msp-shape-empty" />
<use href="#circle-right" className="msp-shape-empty" />
</g>
</svg>;
const _set = <svg width="24px" height="24px" viewBox="0 0 24 24">
<defs>
{circleLeft}
{circleRight}
</defs>
<g>
<use href="#circle-left" className="msp-shape-empty" />
<use href="#circle-right" className="msp-shape-filled" />
</g>
</svg>;
const _molecule = <svg width="17px" height="17px" viewBox="0 0 299.463 299.463">
<g>
<path d="M256.851,173.832v-48.201c22.916-4.918,34.151-30.668,22.556-50.771c-11.547-20.004-39.486-23.251-55.242-5.844 l-41.746-24.106C189.618,22.603,172.861,0,149.734,0c-23.132,0-39.881,22.609-32.685,44.911L75.305,69.016 C59.522,51.586,31.597,54.88,20.061,74.863c-11.63,20.163-0.298,45.862,22.557,50.769v48.2 c-22.821,4.898-34.195,30.591-22.556,50.771c11.529,19.972,39.454,23.285,55.242,5.845l41.746,24.106 c-7.199,22.308,9.559,44.911,32.685,44.911c23.132,0,39.88-22.609,32.685-44.911l41.745-24.106 c15.817,17.469,43.73,14.099,55.242-5.844c0,0,0-0.001,0.001-0.002c4.587-7.953,5.805-17.213,3.431-26.076 C279.392,185.657,269.129,176.461,256.851,173.832z M249.62,72.088c20.568,0,27.428,27.191,10.008,37.239 c-0.003,0.002-0.006,0.003-0.009,0.005c-10.04,5.81-22.85,1.762-27.877-8.475C225.206,87.548,234.938,72.088,249.62,72.088z M149.734,14.4c11.005,0,19.958,8.954,19.958,19.959c0,11.127-9.077,19.958-19.958,19.958c-10.95,0-19.958-8.9-19.958-19.958 C129.776,23.354,138.729,14.4,149.734,14.4z M39.84,109.328c-17.451-10.067-10.534-37.24,10.01-37.24 c15.311,0,24.922,16.653,17.251,29.942C61.681,111.397,49.517,114.925,39.84,109.328z M59.802,224.702 c-9.535,5.503-21.768,2.229-27.268-7.298c-7.639-13.242,1.887-29.945,17.236-29.945c0.013,0,0.027,0,0.04,0 C70.07,187.48,77.49,214.469,59.802,224.702z M149.734,285.062c-11.005,0-19.958-8.954-19.958-19.958 c0-11.127,9.077-19.958,19.958-19.958c10.954,0,19.958,8.903,19.958,19.958C169.693,276.109,160.74,285.062,149.734,285.062z M216.953,217.982l-41.727,24.095c-13.778-15.22-37.459-14.94-50.983,0l-41.728-24.096c6.196-19.289-5.541-39.835-25.498-44.149 V125.63c19.752-4.268,31.762-24.65,25.498-44.149l41.727-24.095c13.629,15.055,37.32,15.093,50.983,0l41.728,24.096 c-6.196,19.29,5.534,39.835,25.498,44.149v48.202C222.61,178.123,210.721,198.581,216.953,217.982z M266.935,217.404 c-5.501,9.528-17.732,12.802-27.261,7.302c-17.682-10.23-10.301-37.247,10.032-37.247 C264.984,187.459,274.602,204.112,266.935,217.404z"/>
</g>
</svg>;
const _ruler = <svg viewBox="0 0 508.073 508.073" width="17px" height="17px">
<g>
<path d="M470.459,378.925c-0.7-2.1-1.9-4-3.4-5.5l-113.9-113.9l149.8-149.8c10-10,2.6-17.3,0-19.9l-85.7-85.7 c-5.5-5.5-14.4-5.5-19.9,0l-149.8,149.8l-134.7-134.7c-25.5-25.5-67.8-25.6-93.4,0c-25.8,25.8-25.8,67.7,0,93.5l134.6,134.7 l-149.9,149.9c-5.5,5.5-5.5,14.4,0,19.9l85.6,85.7c2.6,2.6,10,10,19.9,0l150-149.9l113.9,113.9c1.5,1.5,3.4,2.7,5.5,3.4 l110.4,36.9c6,2,10.7,0.3,14.4-3.4c3.8-3.8,5.1-9.4,3.4-14.4L470.459,378.925z M276.159,165.225l29.9,29.9 c5.5,5.5,14.4,5.5,19.9,0c5-5,5.5-14.4,0-19.9l-29.9-29.9l23.7-23.7l13,13c5.5,5.5,14.4,5.5,19.9,0c5.2-5.2,6.2-13.7,0-19.9 l-13-13l23.7-23.7l29.4,29.4c5.5,5.5,14.4,5.5,19.9,0c5.6-5.6,5.5-14.4,0-19.9l-29.4-29.5l24-24l65.8,65.7l-139.8,139.9 l-65.8-65.8L276.159,165.225z M39.359,92.825c-14.8-14.8-14.8-38.8,0-53.6c14.1-14.1,38.8-14.7,53.6,0l15.5,15.5l-53.6,53.6 L39.359,92.825z M99.759,473.025l-65.7-65.7l24-24l13.2,13.2c5.5,5.5,14.4,5.6,19.9,0c5.5-5.5,5.5-14.4,0-19.9l-13.1-13.3 l23.7-23.7l29.6,29.6c6.2,6.2,14.7,5.2,19.9,0c5.5-5.5,5.5-14.4,0-19.9l-29.6-29.6l23.7-23.7l13.2,13.2c5.5,5.5,14.9,5,19.9,0 c5.5-5.5,5.5-14.4,0-19.9l-13.2-13.2l8.8-8.8l65.8,65.7L99.759,473.025z M74.759,128.225l53.6-53.6l308.7,308.7l-53.6,53.6 L74.759,128.225z M409.659,450.725l41.3-41.3l20.7,61.9L409.659,450.725z"/>
</g>
</svg>;
const _cube = <svg viewBox="0 0 270.06 270.06" width="17px" height="17px">
<g>
<path d="M264.898,0.007 c-0.181,0.006-0.362,0.023-0.541,0.049H84.996c-1.326,0-2.598,0.527-3.535,1.465l-80,80C0.525,82.459-0.001,83.73,0,85.056v180 c0,2.761,2.239,5,5,5h180c1.326,0,2.598-0.527,3.535-1.465l80-80c0.938-0.938,1.465-2.209,1.465-3.535V5.819 c0.14-0.893,0.035-1.807-0.303-2.645c-0.016-0.045-0.033-0.09-0.051-0.135c-0.808-1.89-2.69-3.093-4.744-3.033L264.898,0.007z M87.066,10.056h165.859l-70,70H17.066L87.066,10.056z M259.996,17.126v165.859l-70,70V87.126L259.996,17.126L259.996,17.126z M84.92,19.985c-2.759,0.042-4.963,2.311-4.924,5.07v40c-0.039,2.761,2.168,5.032,4.929,5.071s5.032-2.168,5.071-4.929 c0.001-0.047,0.001-0.094,0-0.141v-40c0.039-2.761-2.168-5.031-4.93-5.07C85.018,19.984,84.969,19.984,84.92,19.985z M9.996,90.056 h170v170h-170V90.056L9.996,90.056z M84.92,99.985c-2.759,0.042-4.963,2.311-4.924,5.07v30c-0.039,2.761,2.168,5.032,4.929,5.071 s5.032-2.168,5.071-4.929c0.001-0.047,0.001-0.094,0-0.141v-30c0.039-2.761-2.168-5.031-4.93-5.07 C85.018,99.984,84.969,99.984,84.92,99.985z M84.92,159.985c-2.759,0.042-4.963,2.311-4.924,5.07v17.93L61.461,201.52 c-1.992,1.913-2.057,5.078-0.144,7.07c1.913,1.992,5.078,2.057,7.07,0.144c0.049-0.047,0.097-0.095,0.144-0.144l18.535-18.535h17.93 c2.761,0.039,5.032-2.168,5.071-4.929s-2.168-5.032-4.929-5.071c-0.047-0.001-0.094-0.001-0.141,0h-15v-15 c0.039-2.761-2.168-5.031-4.93-5.07C85.018,159.984,84.969,159.984,84.92,159.985z M134.996,180.055 c-2.761-0.039-5.032,2.168-5.071,4.929c-0.039,2.761,2.168,5.032,4.929,5.071c0.047,0.001,0.094,0.001,0.141,0h30 c2.761,0.039,5.032-2.168,5.071-4.929c0.039-2.761-2.168-5.032-4.929-5.071c-0.047-0.001-0.094-0.001-0.141,0H134.996z M204.996,180.055c-2.761-0.039-5.032,2.168-5.071,4.929c-0.039,2.761,2.168,5.032,4.929,5.071c0.047,0.001,0.094,0.001,0.141,0h40 c2.761,0.039,5.032-2.168,5.071-4.929s-2.168-5.032-4.929-5.071c-0.047-0.001-0.094-0.001-0.141,0H204.996z M44.898,220.007 c-1.299,0.039-2.532,0.582-3.438,1.514l-20,20c-1.992,1.913-2.057,5.078-0.144,7.07c1.913,1.992,5.078,2.057,7.07,0.144 c0.049-0.047,0.097-0.095,0.144-0.144l20-20c1.98-1.925,2.025-5.091,0.1-7.071C47.654,220.514,46.3,219.965,44.898,220.007z"/>
</g>
</svg>;
// Icons below are adapted from https://materialdesignicons.com/ and
// The following icons are adapted from https://materialdesignicons.com/ and
// licensed with https://github.com/Templarian/MaterialDesign/blob/master/LICENSE
const _cursor = <svg width="24px" height="24px" viewBox="0 0 24 24">
<path d="M10.07,14.27C10.57,14.03 11.16,14.25 11.4,14.75L13.7,19.74L15.5,18.89L13.19,13.91C12.95,13.41 13.17,12.81 13.67,12.58L13.95,12.5L16.25,12.05L8,5.12V15.9L9.82,14.43L10.07,14.27M13.64,21.97C13.14,22.21 12.54,22 12.31,21.5L10.13,16.76L7.62,18.78C7.45,18.92 7.24,19 7,19A1,1 0 0,1 6,18V3A1,1 0 0,1 7,2C7.24,2 7.47,2.09 7.64,2.23L7.65,2.22L19.14,11.86C19.57,12.22 19.62,12.85 19.27,13.27C19.12,13.45 18.91,13.57 18.7,13.61L15.54,14.23L17.74,18.96C18,19.46 17.76,20.05 17.26,20.28L13.64,21.97Z" />
</svg>;
const _CubeOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z" /></svg>;
export function CubeOutlineSvg() { return _CubeOutline; }
const _CursorDefaultOutline = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M10.07,14.27C10.57,14.03 11.16,14.25 11.4,14.75L13.7,19.74L15.5,18.89L13.19,13.91C12.95,13.41 13.17,12.81 13.67,12.58L13.95,12.5L16.25,12.05L8,5.12V15.9L9.82,14.43L10.07,14.27M13.64,21.97C13.14,22.21 12.54,22 12.31,21.5L10.13,16.76L7.62,18.78C7.45,18.92 7.24,19 7,19A1,1 0 0,1 6,18V3A1,1 0 0,1 7,2C7.24,2 7.47,2.09 7.64,2.23L7.65,2.22L19.14,11.86C19.57,12.22 19.62,12.85 19.27,13.27C19.12,13.45 18.91,13.57 18.7,13.61L15.54,14.23L17.74,18.96C18,19.46 17.76,20.05 17.26,20.28L13.64,21.97Z' /></svg>;
export function CursorDefaultOutlineSvg() { return _CursorDefaultOutline; }
const _FileOutline = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path fill='currentColor' d='M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z' /></svg>;
export function FileOutlineSvg() { return _FileOutline; }
const _PencilRuler = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M3 17.25V21H6.75L17.81 9.93L14.06 6.18L3 17.25M22.61 18.36L18.36 22.61L13.16 17.41L14.93 15.64L15.93 16.64L18.4 14.16L19.82 15.58L18.36 17L19.42 18L20.84 16.6L22.61 18.36M6.61 10.83L1.39 5.64L5.64 1.39L7.4 3.16L4.93 5.64L6 6.7L8.46 4.22L9.88 5.64L8.46 7.05L9.46 8.05L6.61 10.83M20.71 7C21.1 6.61 21.1 6 20.71 5.59L18.37 3.29C18 2.9 17.35 2.9 16.96 3.29L15.12 5.12L18.87 8.87L20.71 7Z" /></svg>;
export function PencilRulerSvg() { return _PencilRuler; }
// The following icons are adapted from https://material-ui.com/components/material-icons/ and
// licensed with https://github.com/mui-org/material-ui/blob/master/LICENSE
const _AccountTreeOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M22 11V3h-7v3H9V3H2v8h7V8h2v10h4v3h7v-8h-7v3h-2V8h2v3h7zM7 9H4V5h3v4zm10 6h3v4h-3v-4zm0-10h3v4h-3V5z' /></svg>;
export function AccountTreeOutlinedSvg() { return _AccountTreeOutlined; }
const _Add = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z' /></svg>;
export function AddSvg() { return _Add; }
const _ArrowDownward = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z' /></svg>;
export function ArrowDownwardSvg() { return _ArrowDownward; }
const _ArrowDropDown = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M7 10l5 5 5-5z' /></svg>;
export function ArrowDropDownSvg() { return _ArrowDropDown; }
const _ArrowRight = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M10 17l5-5-5-5v10z' /></svg>;
export function ArrowRightSvg() { return _ArrowRight; }
const _ArrowUpward = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z' /></svg>;
export function ArrowUpwardSvg() { return _ArrowUpward; }
const _Autorenew = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 6v3l4-4-4-4v3c-4.42 0-8 3.58-8 8 0 1.57.46 3.03 1.24 4.26L6.7 14.8c-.45-.83-.7-1.79-.7-2.8 0-3.31 2.69-6 6-6zm6.76 1.74L17.3 9.2c.44.84.7 1.79.7 2.8 0 3.31-2.69 6-6 6v-3l-4 4 4 4v-3c4.42 0 8-3.58 8-8 0-1.57-.46-3.03-1.24-4.26z' /></svg>;
export function AutorenewSvg() { return _Autorenew; }
const _BlurOn = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M6 13c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm-3 .5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM6 5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm15 5.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM14 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0-3.5c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zm-11 10c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm7 7c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm0-17c.28 0 .5-.22.5-.5s-.22-.5-.5-.5-.5.22-.5.5.22.5.5.5zM10 7c.55 0 1-.45 1-1s-.45-1-1-1-1 .45-1 1 .45 1 1 1zm0 5.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm8 .5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-8c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0-4c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm3 8.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zM14 17c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm0 3.5c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-4-12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0 8.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1zm4-4.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-4c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z' /></svg>;
export function BlurOnSvg() { return _BlurOn; }
const _BookmarksOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M15 7v12.97l-4.21-1.81-.79-.34-.79.34L5 19.97V7h10m4-6H8.99C7.89 1 7 1.9 7 3h10c1.1 0 2 .9 2 2v13l2 1V3c0-1.1-.9-2-2-2zm-4 4H5c-1.1 0-2 .9-2 2v16l7-3 7 3V7c0-1.1-.9-2-2-2z' /></svg>;
export function BookmarksOutlinedSvg() { return _BookmarksOutlined; }
const _Brush = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2 2.21 0 4-1.79 4-4 0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34a.9959.9959 0 00-1.41 0L9 12.25 11.75 15l8.96-8.96c.39-.39.39-1.02 0-1.41z' /></svg>;
export function BrushSvg() { return _Brush; }
const _BuildOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M22.61 18.99l-9.08-9.08c.93-2.34.45-5.1-1.44-7C9.79.61 6.21.4 3.66 2.26L7.5 6.11 6.08 7.52 2.25 3.69C.39 6.23.6 9.82 2.9 12.11c1.86 1.86 4.57 2.35 6.89 1.48l9.11 9.11c.39.39 1.02.39 1.41 0l2.3-2.3c.4-.38.4-1.01 0-1.41zm-3 1.6l-9.46-9.46c-.61.45-1.29.72-2 .82-1.36.2-2.79-.21-3.83-1.25C3.37 9.76 2.93 8.5 3 7.26l3.09 3.09 4.24-4.24-3.09-3.09c1.24-.07 2.49.37 3.44 1.31 1.08 1.08 1.49 2.57 1.24 3.96-.12.71-.42 1.37-.88 1.96l9.45 9.45-.88.89z' /></svg>;
export function BuildOutlinedSvg() { return _BuildOutlined; }
const _Build = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z' /></svg>;
export function BuildSvg() { return _Build; }
const _CameraOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M14.25 2.26l-.08-.04-.01.02C13.46 2.09 12.74 2 12 2 6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10c0-4.75-3.31-8.72-7.75-9.74zM19.41 9h-7.99l2.71-4.7c2.4.66 4.35 2.42 5.28 4.7zM13.1 4.08L10.27 9l-1.15 2L6.4 6.3C7.84 4.88 9.82 4 12 4c.37 0 .74.03 1.1.08zM5.7 7.09L8.54 12l1.15 2H4.26C4.1 13.36 4 12.69 4 12c0-1.85.64-3.55 1.7-4.91zM4.59 15h7.98l-2.71 4.7c-2.4-.67-4.34-2.42-5.27-4.7zm6.31 4.91L14.89 13l2.72 4.7C16.16 19.12 14.18 20 12 20c-.38 0-.74-.04-1.1-.09zm7.4-3l-4-6.91h5.43c.17.64.27 1.31.27 2 0 1.85-.64 3.55-1.7 4.91z' /></svg>;
export function CameraOutlinedSvg() { return _CameraOutlined; }
const _Camera = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M9.4 10.5l4.77-8.26C13.47 2.09 12.75 2 12 2c-2.4 0-4.6.85-6.32 2.25l3.66 6.35.06-.1zM21.54 9c-.92-2.92-3.15-5.26-6-6.34L11.88 9h9.66zm.26 1h-7.49l.29.5 4.76 8.25C21 16.97 22 14.61 22 12c0-.69-.07-1.35-.2-2zM8.54 12l-3.9-6.75C3.01 7.03 2 9.39 2 12c0 .69.07 1.35.2 2h7.49l-1.15-2zm-6.08 3c.92 2.92 3.15 5.26 6 6.34L12.12 15H2.46zm11.27 0l-3.9 6.76c.7.15 1.42.24 2.17.24 2.4 0 4.6-.85 6.32-2.25l-3.66-6.35-.93 1.6z' /></svg>;
export function CameraSvg() { return _Camera; }
const _CancelOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3.59-13L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41z' /></svg>;
export function CancelOutlinedSvg() { return _CancelOutlined; }
const _Cancel = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z' /></svg>;
export function CancelSvg() { return _Cancel; }
const _CenterFocusStrong = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm-7 7H3v4c0 1.1.9 2 2 2h4v-2H5v-4zM5 5h4V3H5c-1.1 0-2 .9-2 2v4h2V5zm14-2h-4v2h4v4h2V5c0-1.1-.9-2-2-2zm0 16h-4v2h4c1.1 0 2-.9 2-2v-4h-2v4z' /></svg>;
export function CenterFocusStrongSvg() { return _CenterFocusStrong; }
const _Check = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z' /></svg>;
export function CheckSvg() { return _Check; }
const _Clear = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z' /></svg>;
export function ClearSvg() { return _Clear; }
const _Close = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z' /></svg>;
export function CloseSvg() { return _Close; }
const _CloudUpload = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z' /></svg>;
export function CloudUploadSvg() { return _CloudUpload; }
const _Code = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z' /></svg>;
export function CodeSvg() { return _Code; }
const _DeleteOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z' /></svg>;
export function DeleteOutlinedSvg() { return _DeleteOutlined; }
const _Delete = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z' /></svg>;
export function DeleteSvg() { return _Delete; }
const _Error = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z' /></svg>;
export function ErrorSvg() { return _Error; }
const _Extension = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z' /></svg>;
export function ExtensionSvg() { return _Extension; }
const _FlipToFront = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.89 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9c-1.11 0-2 .9-2 2v10c0 1.1.89 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12H9V5h10v10zm-8 6h2v-2h-2v2zm-4 0h2v-2H7v2z' /></svg>;
export function FlipToFrontSvg() { return _FlipToFront; }
const _Fullscreen = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z' /></svg>;
export function FullscreenSvg() { return _Fullscreen; }
const _GetApp = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z' /></svg>;
export function GetAppSvg() { return _GetApp; }
const _HelpOutline = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z' /></svg>;
export function HelpOutlineSvg() { return _HelpOutline; }
const _HomeOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 5.69l5 4.5V18h-2v-6H9v6H7v-7.81l5-4.5M12 3L2 12h3v8h6v-6h2v6h6v-8h3L12 3z' /></svg>;
export function HomeOutlinedSvg() { return _HomeOutlined; }
const _Launch = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z' /></svg>;
export function LaunchSvg() { return _Launch; }
const _LinearScale = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19.5 9.5c-1.03 0-1.9.62-2.29 1.5h-2.92c-.39-.88-1.26-1.5-2.29-1.5s-1.9.62-2.29 1.5H6.79c-.39-.88-1.26-1.5-2.29-1.5C3.12 9.5 2 10.62 2 12s1.12 2.5 2.5 2.5c1.03 0 1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5s1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5 1.38 0 2.5-1.12 2.5-2.5s-1.12-2.5-2.5-2.5z' /></svg>;
export function LinearScaleSvg() { return _LinearScale; }
const _MoreHoriz = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z' /></svg>;
export function MoreHorizSvg() { return _MoreHoriz; }
const _NavigateBefore = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z' /></svg>;
export function NavigateBeforeSvg() { return _NavigateBefore; }
const _NavigateNext = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z' /></svg>;
export function NavigateNextSvg() { return _NavigateNext; }
const _OpenInBrowser = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z' /></svg>;
export function OpenInBrowserSvg() { return _OpenInBrowser; }
const _PlayArrow = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M8 5v14l11-7z' /></svg>;
export function PlayArrowSvg() { return _PlayArrow; }
const _Refresh = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z' /></svg>;
export function RefreshSvg() { return _Refresh; }
const _Remove = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M19 13H5v-2h14v2z' /></svg>;
export function RemoveSvg() { return _Remove; }
const _Restore = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8H12z' /></svg>;
export function RestoreSvg() { return _Restore; }
const _SaveOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d='M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm2 16H5V5h11.17L19 7.83V19zm-7-7c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zM6 6h9v4H6z' /></svg>;
export function SaveOutlinedSvg() { return _SaveOutlined; }
const _ScatterPlot = <svg width='24px' height='24px' viewBox='0 0 24 24'><circle cx='7' cy='14' r='3' /><circle cx='11' cy='6' r='3' /><circle cx='16.6' cy='17.6' r='3' /></svg>;
export function ScatterPlotSvg() { return _ScatterPlot; }
const _SkipPrevious = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M6 6h2v12H6zm3.5 6l8.5 6V6z' /></svg>;
export function SkipPreviousSvg() { return _SkipPrevious; }
const _Stop = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M6 6h12v12H6z' /></svg>;
export function StopSvg() { return _Stop; }
const _SubscriptionsOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M4 6h16v2H4zm2-4h12v2H6zm14 8H4c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-8c0-1.1-.9-2-2-2zm0 10H4v-8h16v8zm-10-7.27v6.53L16 16z' /></svg>;
export function SubscriptionsOutlinedSvg() { return _SubscriptionsOutlined; }
const _SwapHoriz = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z' /></svg>;
export function SwapHorizSvg() { return _SwapHoriz; }
const _Tune = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z' /></svg>;
export function TuneSvg() { return _Tune; }
const _VisibilityOffOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2.41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6zm-1.07 1.14L13 9.21c.57.25 1.03.71 1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07.14zM2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45 2.01 3.87zm7.5 7.5l2.61 2.61c-.04.01-.08.02-.12.02-1.38 0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13zm-3.4-3.4l1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53z' /></svg>;
export function VisibilityOffOutlinedSvg() { return _VisibilityOffOutlined; }
const _VisibilityOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M12 6c3.79 0 7.17 2.13 8.82 5.5C19.17 14.87 15.79 17 12 17s-7.17-2.13-8.82-5.5C4.83 8.13 8.21 6 12 6m0-2C7 4 2.73 7.11 1 11.5 2.73 15.89 7 19 12 19s9.27-3.11 11-7.5C21.27 7.11 17 4 12 4zm0 5c1.38 0 2.5 1.12 2.5 2.5S13.38 14 12 14s-2.5-1.12-2.5-2.5S10.62 9 12 9m0-2c-2.48 0-4.5 2.02-4.5 4.5S9.52 16 12 16s4.5-2.02 4.5-4.5S14.48 7 12 7z' /></svg>;
export function VisibilityOutlinedSvg() { return _VisibilityOutlined; }
const _Warning = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z' /></svg>;
export function WarningSvg() { return _Warning; }
// Aliases
export const SelectionModeSvg = CursorDefaultOutlineSvg;
export const SuperposeAtomsSvg = ScatterPlotSvg;
export const SuperposeChainsSvg = LinearScaleSvg;
export const SuperpositionSvg = FlipToFrontSvg;

View File

@@ -5,16 +5,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import ArrowRight from '@material-ui/icons/ArrowRight';
import ArrowUpward from '@material-ui/icons/ArrowUpward';
import BookmarksOutlined from '@material-ui/icons/BookmarksOutlined';
import Check from '@material-ui/icons/Check';
import Clear from '@material-ui/icons/Clear';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import HelpOutline from '@material-ui/icons/HelpOutline';
import MoreHoriz from '@material-ui/icons/MoreHoriz';
import * as React from 'react';
import { Mat4, Vec2, Vec3 } from '../../mol-math/linear-algebra';
import { PluginContext } from '../../mol-plugin/context';
@@ -30,7 +20,7 @@ import { PluginUIComponent } from '../base';
import { ActionMenu } from './action-menu';
import { ColorOptions, ColorValueOption, CombinedColorControl } from './color';
import { Button, ControlGroup, ControlRow, ExpandGroup, IconButton, TextInput, ToggleButton } from './common';
import { Icon } from './icons';
import { Icon, HelpOutlineSvg, CheckSvg, ClearSvg, BookmarksOutlinedSvg, MoreHorizSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowDownwardSvg, ArrowUpwardSvg, DeleteOutlinedSvg } from './icons';
import { legendFor } from './legend';
import LineGraphComponent from './line-graph/line-graph-component';
import { Slider, Slider2 } from './slider';
@@ -216,7 +206,7 @@ export class ParamHelp<L extends LegendData> extends React.PureComponent<{ legen
return <div className='msp-help-text'>
<div>
<div className='msp-help-description'><Icon svg={HelpOutline} inline />{description}</div>
<div className='msp-help-description'><Icon svg={HelpOutlineSvg} inline />{description}</div>
{Legend && <div className='msp-help-legend'><Legend legend={legend} /></div>}
</div>
</div>;
@@ -259,8 +249,7 @@ function renderSimple(options: { props: ParamProps<any>, state: { showHelp: bool
<button className='msp-help msp-btn-link msp-btn-icon msp-control-group-expander' onClick={toggleHelp}
title={desc || `${state.showHelp ? 'Hide' : 'Show'} help`}
style={{ background: 'transparent', textAlign: 'left', padding: '0' }}>
{/* TODO: <Icon name={state.showHelp ? 'help-circle-collapse' : 'help-circle-expand'} /> */}
<Icon svg={HelpOutline} />
<Icon svg={HelpOutlineSvg} />
</button>
}
</>}
@@ -300,7 +289,7 @@ export class BoolControl extends SimpleParam<PD.BooleanParam> {
onClick = (e: React.MouseEvent<HTMLButtonElement>) => { this.update(!this.props.value); e.currentTarget.blur(); }
renderControl() {
return <button onClick={this.onClick} disabled={this.props.isDisabled}>
<Icon svg={this.props.value ? Check : Clear} />
<Icon svg={this.props.value ? CheckSvg : ClearSvg} />
{this.props.value ? 'On' : 'Off'}
</button>;
}
@@ -466,8 +455,8 @@ export class SelectControl extends React.PureComponent<ParamProps<PD.Select<stri
const toggle = this.props.param.cycle ? this.cycle : this.toggle;
const textAlign = this.props.param.cycle ? 'center' : 'left';
const icon = this.props.param.cycle
? (this.props.value === 'on' ? Check
: this.props.value === 'off' ? Clear : void 0)
? (this.props.value === 'on' ? CheckSvg
: this.props.value === 'off' ? ClearSvg : void 0)
: void 0;
return <ToggleButton disabled={this.props.isDisabled} style={{ textAlign, overflow: 'hidden', textOverflow: 'ellipsis' }}
@@ -644,7 +633,7 @@ export class ColorListControl extends React.PureComponent<ParamProps<PD.ColorLis
{value.colors.length === 1 ? '1 color' : `${value.colors.length} colors`}
<div style={colorStripStyle(value, '33px')} />
</button>
<IconButton svg={BookmarksOutlined} onClick={this.togglePresets} toggleState={this.state.show === 'presets'} title='Color Presets'
<IconButton svg={BookmarksOutlinedSvg} onClick={this.togglePresets} toggleState={this.state.show === 'presets'} title='Color Presets'
style={{ padding: 0, position: 'absolute', right: 0, top: 0, width: '32px' }} />
</>;
}
@@ -939,7 +928,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
if (!this.state.isExpanded) {
return <div className='msp-mapped-parameter-group'>
{ctrl}
<IconButton svg={MoreHoriz} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
<IconButton svg={MoreHorizSvg} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
</div>;
}
@@ -950,7 +939,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
return <div className='msp-mapped-parameter-group'>
{ctrl}
<IconButton svg={MoreHoriz} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
<IconButton svg={MoreHorizSvg} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
<div className='msp-control-offset'>
<ParameterControls params={filtered} onEnter={this.props.onEnter} values={this.props.value} onChange={this.onChangeParam} isDisabled={this.props.isDisabled} />
</div>
@@ -980,7 +969,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
return <div className='msp-control-group-wrapper'>
<div className='msp-control-group-header'>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleExpanded}>
<Icon svg={this.state.isExpanded ? ArrowDropDown : ArrowRight} />
<Icon svg={this.state.isExpanded ? ArrowDropDownSvg : ArrowRightSvg} />
{label}
</button>
</div>
@@ -1056,7 +1045,7 @@ export class MappedControl extends React.PureComponent<ParamProps<PD.Mapped<any>
if (!this.areParamsEmpty(param.params)) {
return <div className='msp-mapped-parameter-group'>
{Select}
<IconButton svg={MoreHoriz} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`${label} Properties`} />
<IconButton svg={MoreHorizSvg} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`${label} Properties`} />
{this.state.isExpanded && <GroupControl inMapped param={param} value={value.params} name={value.name} onChange={this.onChangeParam} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />}
</div>;
}
@@ -1104,7 +1093,7 @@ class ObjectListItem extends React.PureComponent<ObjectListItemProps, { isExpand
state = { isExpanded: false };
update = (v: object) => {
this.setState({ isExpanded: false });
// this.setState({ isExpanded: false }); // TODO auto update? mark changed state?
this.props.actions.update(v, this.props.index);
}
@@ -1134,9 +1123,9 @@ class ObjectListItem extends React.PureComponent<ObjectListItemProps, { isExpand
{this.props.param.getLabel(this.props.value)}
</button>
<div>
<IconButton svg={ArrowDownward} title='Move Up' onClick={this.moveUp} small={true} />
<IconButton svg={ArrowUpward} title='Move Down' onClick={this.moveDown} small={true} />
<IconButton svg={DeleteOutlined} title='Remove' onClick={this.remove} small={true} />
<IconButton svg={ArrowDownwardSvg} title='Move Up' onClick={this.moveUp} small={true} />
<IconButton svg={ArrowUpwardSvg} title='Move Down' onClick={this.moveDown} small={true} />
<IconButton svg={DeleteOutlinedSvg} title='Remove' onClick={this.remove} small={true} />
</div>
</div>
{this.state.isExpanded && <div className='msp-control-offset'>

View File

@@ -16,10 +16,9 @@ import { Volume, Grid } from '../../mol-model/volume';
import { Vec3 } from '../../mol-math/linear-algebra';
import { ColorNames } from '../../mol-util/color/names';
import { toPrecision } from '../../mol-util/number';
import VisibilityOffOutlined from '@material-ui/icons/VisibilityOffOutlined';
import VisibilityOutlined from '@material-ui/icons/VisibilityOutlined';
import { StateSelection, StateObjectCell } from '../../mol-state';
import { setSubtreeVisibility } from '../../mol-plugin/behavior/static/state';
import { VisibilityOutlinedSvg, VisibilityOffOutlinedSvg } from '../controls/icons';
const ChannelParams = {
color: PD.Color(ColorNames.black, { description: 'Display color of the volume.' }),
@@ -107,7 +106,7 @@ class Channel extends PluginUIComponent<{
pivot={<div className='msp-volume-channel-inline-controls'>
<Slider value={value} min={ctrlMin} max={ctrlMax} step={step}
onChange={v => props.changeIso(props.name, v, isRelative)} disabled={props.params.isDisabled} onEnter={props.params.events.onEnter} />
<IconButton svg={this.getVisible() ? VisibilityOutlined : VisibilityOffOutlined} onClick={this.toggleVisible} toggleState={false} disabled={props.params.isDisabled} />
<IconButton svg={this.getVisible() ? VisibilityOutlinedSvg : VisibilityOffOutlinedSvg} onClick={this.toggleVisible} toggleState={false} disabled={props.params.isDisabled} />
</div>}
controls={<ParameterControls onChange={({ name, value }) => props.changeParams(props.name, name, value)} params={ChannelParams} values={channel} onEnter={props.params.events.onEnter} isDisabled={props.params.isDisabled} />}
/>;

View File

@@ -4,12 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import AccountTreeOutlined from '@material-ui/icons/AccountTreeOutlined';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import HelpOutline from '@material-ui/icons/HelpOutline';
import HomeOutlined from '@material-ui/icons/HomeOutlined';
import SaveOutlined from '@material-ui/icons/SaveOutlined';
import Tune from '@material-ui/icons/Tune';
import * as React from 'react';
import { Canvas3DParams } from '../mol-canvas3d/canvas3d';
import { PluginCommands } from '../mol-plugin/commands';
@@ -23,6 +17,7 @@ import { StateObjectActions } from './state/actions';
import { RemoteStateSnapshots, StateSnapshots } from './state/snapshots';
import { StateTree } from './state/tree';
import { HelpContent } from './viewport/help';
import { HomeOutlinedSvg, AccountTreeOutlinedSvg, TuneSvg, HelpOutlineSvg, SaveOutlinedSvg, DeleteOutlinedSvg } from './controls/icons';
export class LeftPanelControls extends PluginUIComponent<{}, { tab: LeftPanelTabName }> {
state = { tab: this.plugin.behaviors.layout.leftPanelTabName.value };
@@ -57,21 +52,21 @@ export class LeftPanelControls extends PluginUIComponent<{}, { tab: LeftPanelTab
tabs: { [K in LeftPanelTabName]: JSX.Element } = {
'none': <></>,
'root': <>
<SectionHeader icon={HomeOutlined} title='Home' />
<SectionHeader icon={HomeOutlinedSvg} title='Home' />
<StateObjectActions state={this.plugin.state.data} nodeRef={StateTransform.RootRef} hideHeader={true} initiallyCollapsed={true} alwaysExpandFirst={true} />
{this.plugin.spec.components?.remoteState !== 'none' && <RemoteStateSnapshots listOnly /> }
</>,
'data': <>
<SectionHeader icon={AccountTreeOutlined} title={<><RemoveAllButton /> State Tree</>} />
<SectionHeader icon={AccountTreeOutlinedSvg} title={<><RemoveAllButton /> State Tree</>} />
<StateTree state={this.plugin.state.data} />
</>,
'states': <StateSnapshots />,
'settings': <>
<SectionHeader icon={Tune} title='Plugin Settings' />
<SectionHeader icon={TuneSvg} title='Plugin Settings' />
<FullSettings />
</>,
'help': <>
<SectionHeader icon={HelpOutline} title='Help' />
<SectionHeader icon={HelpOutlineSvg} title='Help' />
<HelpContent />
</>
}
@@ -81,12 +76,12 @@ export class LeftPanelControls extends PluginUIComponent<{}, { tab: LeftPanelTab
return <div className='msp-left-panel-controls'>
<div className='msp-left-panel-controls-buttons'>
<IconButton svg={HomeOutlined} toggleState={tab === 'root'} transparent onClick={() => this.set('root')} title='Home' />
<IconButton svg={HomeOutlinedSvg} toggleState={tab === 'root'} transparent onClick={() => this.set('root')} title='Home' />
<DataIcon set={this.set} />
<IconButton svg={SaveOutlined} toggleState={tab === 'states'} transparent onClick={() => this.set('states')} title='Plugin State' />
<IconButton svg={HelpOutline} toggleState={tab === 'help'} transparent onClick={() => this.set('help')} title='Help' />
<IconButton svg={SaveOutlinedSvg} toggleState={tab === 'states'} transparent onClick={() => this.set('states')} title='Plugin State' />
<IconButton svg={HelpOutlineSvg} toggleState={tab === 'help'} transparent onClick={() => this.set('help')} title='Help' />
<div className='msp-left-panel-controls-buttons-bottom'>
<IconButton svg={Tune} toggleState={tab === 'settings'} transparent onClick={() => this.set('settings')} title='Settings' />
<IconButton svg={TuneSvg} toggleState={tab === 'settings'} transparent onClick={() => this.set('settings')} title='Settings' />
</div>
</div>
<div className='msp-scrollable-container'>
@@ -116,7 +111,7 @@ class DataIcon extends PluginUIComponent<{ set: (tab: LeftPanelTabName) => void
render() {
return <IconButton
svg={AccountTreeOutlined} toggleState={this.tab === 'data'} transparent onClick={() => this.props.set('data')} title='State Tree'
svg={AccountTreeOutlinedSvg} toggleState={this.tab === 'data'} transparent onClick={() => this.props.set('data')} title='State Tree'
style={{ position: 'relative' }} extraContent={this.state.changed ? <div className='msp-left-panel-controls-button-data-dirty' /> : void 0} />;
}
}
@@ -168,6 +163,6 @@ class RemoveAllButton extends PluginUIComponent<{ }> {
render() {
const count = this.plugin.state.data.tree.children.get(StateTransform.RootRef).size;
if (count === 0) return null;
return <IconButton svg={DeleteOutlined} onClick={this.remove} title={'Remove All'} style={{ display: 'inline-block' }} small className='msp-no-hover-outline' transparent />;
return <IconButton svg={DeleteOutlinedSvg} onClick={this.remove} title={'Remove All'} style={{ display: 'inline-block' }} small className='msp-no-hover-outline' transparent />;
}
}

View File

@@ -20,9 +20,8 @@ import { State, StateSelection } from '../mol-state';
import { ChainSequenceWrapper } from './sequence/chain';
import { ElementSequenceWrapper } from './sequence/element';
import { elementLabel } from '../mol-theme/label';
import { Icon } from './controls/icons';
import { Icon, HelpOutlineSvg } from './controls/icons';
import { StructureSelectionManager } from '../mol-plugin-state/manager/structure/selection';
import HelpOutline from '@material-ui/icons/HelpOutline';
const MaxDisplaySequenceLength = 5000;
@@ -296,7 +295,7 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
if (this.getStructure(this.state.structureRef) === Structure.Empty) {
return <div className='msp-sequence'>
<div className='msp-sequence-select'>
<Icon svg={HelpOutline} style={{ cursor: 'help', position: 'absolute', right: 0, top: 0 }}
<Icon svg={HelpOutlineSvg} style={{ cursor: 'help', position: 'absolute', right: 0, top: 0 }}
title='This shows a single sequence. Use the controls to show a different sequence.'/>
<span>Sequence</span><span style={{ fontWeight: 'normal' }}>No structure available</span>
@@ -311,7 +310,7 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
return <div className='msp-sequence'>
<div className='msp-sequence-select'>
<Icon svg={HelpOutline} style={{ cursor: 'help', position: 'absolute', right: 0, top: 0 }}
<Icon svg={HelpOutlineSvg} style={{ cursor: 'help', position: 'absolute', right: 0, top: 0 }}
title='This shows a single sequence. Use the controls to show a different sequence.' />
<span>Sequence of</span>

View File

@@ -1,5 +1,4 @@
// @import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,700);
@mixin user-select($select) {
-webkit-user-select: $select;
-moz-user-select: $select;

View File

@@ -7,9 +7,8 @@
import * as React from 'react';
import { State } from '../../mol-state';
import { PluginUIComponent } from '../base';
import { Icon } from '../controls/icons';
import { Icon, CodeSvg } from '../controls/icons';
import { ApplyActionControl } from './apply-action';
import Code from '@material-ui/icons/Code';
export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRef: string, hideHeader?: boolean, initiallyCollapsed?: boolean, alwaysExpandFirst?: boolean }> {
get current() {
@@ -42,7 +41,7 @@ export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRe
const display = cell.obj ? cell.obj.label : (def.display && def.display.name) || def.name;
return <div className='msp-state-actions'>
{!this.props.hideHeader && <div className='msp-section-header'><Icon svg={Code} /> {`Actions (${display})`}</div> }
{!this.props.hideHeader && <div className='msp-section-header'><Icon svg={CodeSvg} /> {`Actions (${display})`}</div> }
{actions.map((act, i) => <ApplyActionControl
key={`${act.id}`} state={state} action={act} nodeRef={ref}
initiallyCollapsed={i === 0 ? !this.props.alwaysExpandFirst && this.props.initiallyCollapsed : this.props.initiallyCollapsed} />)}

View File

@@ -8,7 +8,7 @@ import * as React from 'react';
import { PluginUIComponent } from '../base';
import { ParameterControls, ParamOnChange } from '../controls/parameters';
import { Button } from '../controls/common';
import PlayArrow from '@material-ui/icons/PlayArrow';
import { PlayArrowSvg } from '../controls/icons';
export class AnimationControls extends PluginUIComponent<{ onStart?: () => void }> {
componentDidMount() {
@@ -44,7 +44,7 @@ export class AnimationControls extends PluginUIComponent<{ onStart?: () => void
<ParameterControls params={anim.current.params} values={anim.current.paramValues} onChange={this.updateCurrentParams} isDisabled={isDisabled} />
<div className='msp-flex-row'>
<Button icon={anim.state.animationState !== 'playing' ? void 0 : PlayArrow} onClick={this.startOrStop} disabled={canApply !== void 0 && canApply.canApply === false}>
<Button icon={anim.state.animationState !== 'playing' ? void 0 : PlayArrowSvg} onClick={this.startOrStop} disabled={canApply !== void 0 && canApply.canApply === false}>
{anim.state.animationState === 'playing' ? 'Stop' : canApply === void 0 || canApply.canApply ? 'Start' : canApply.reason || 'Start'}
</Button>
</div>

View File

@@ -11,13 +11,8 @@ import { ParameterControls, ParamOnChange } from '../controls/parameters';
import { PluginContext } from '../../mol-plugin/context';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Subject } from 'rxjs';
import { Icon } from '../controls/icons';
import { Icon, RefreshSvg, CheckSvg, ArrowRightSvg, ArrowDropDownSvg, TuneSvg } from '../controls/icons';
import { ExpandGroup, ToggleButton, Button, IconButton } from '../controls/common';
import Refresh from '@material-ui/icons/Refresh';
import ArrowRight from '@material-ui/icons/ArrowRight';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import Check from '@material-ui/icons/Check';
import Tune from '@material-ui/icons/Tune';
export { StateTransformParameters, TransformControlBase };
@@ -199,9 +194,9 @@ abstract class TransformControlBase<P, S extends TransformControlBase.ComponentS
if (this.props.autoHideApply && (!canApply || this.canAutoApply(this.state.params))) return null;
return <div className='msp-transform-apply-wrap'>
<IconButton svg={Refresh} className='msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} title='Set default params' />
<IconButton svg={RefreshSvg} className='msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} title='Set default params' />
<div className={`msp-transform-apply-wider`}>
<Button icon={canApply ? Check : void 0} className={`msp-btn-commit msp-btn-commit-${canApply ? 'on' : 'off'}`} onClick={this.apply} disabled={!canApply}>
<Button icon={canApply ? CheckSvg : void 0} className={`msp-btn-commit msp-btn-commit-${canApply ? 'on' : 'off'}`} onClick={this.apply} disabled={!canApply}>
{this.props.applyLabel || this.applyText()}
</Button>
</div>
@@ -236,7 +231,7 @@ abstract class TransformControlBase<P, S extends TransformControlBase.ComponentS
const ctrl = <div className={wrapClass} style={{ marginBottom: this.props.noMargin ? 0 : void 0 }}>
{display !== 'none' && !this.props.wrapInExpander && <div className='msp-transform-header'>
<Button onClick={this.toggleExpanded} title={display.description}>
{!isEmpty && <Icon svg={this.state.isCollapsed ? ArrowRight : ArrowDropDown} />}
{!isEmpty && <Icon svg={this.state.isCollapsed ? ArrowRightSvg : ArrowDropDownSvg} />}
{display.name}
</Button>
</div>}
@@ -258,7 +253,7 @@ abstract class TransformControlBase<P, S extends TransformControlBase.ComponentS
<Button icon={this.props.simpleApply?.icon} title={this.props.simpleApply?.title} disabled={this.state.busy || !canApply} onClick={this.apply} className='msp-btn-apply-simple'>
{this.props.simpleApply?.header}
</Button>
{!info.isEmpty && <ToggleButton icon={Tune} label='' title='Options' toggle={this.toggleExpanded} isSelected={!this.state.isCollapsed} disabled={this.state.busy} style={{ flex: '0 0 40px', padding: 0 }} />}
{!info.isEmpty && <ToggleButton icon={TuneSvg} label='' title='Options' toggle={this.toggleExpanded} isSelected={!this.state.isCollapsed} disabled={this.state.busy} style={{ flex: '0 0 40px', padding: 0 }} />}
</div>;
if (this.state.isCollapsed) return apply;

View File

@@ -4,17 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Add from '@material-ui/icons/Refresh';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import ArrowUpward from '@material-ui/icons/ArrowUpward';
import CloudUpload from '@material-ui/icons/CloudUpload';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import GetApp from '@material-ui/icons/GetApp';
import OpenInBrowser from '@material-ui/icons/OpenInBrowser';
import SaveOutlined from '@material-ui/icons/SaveOutlined';
import SwapHoriz from '@material-ui/icons/SwapHoriz';
import Refresh from '@material-ui/icons/Refresh';
import Warning from '@material-ui/icons/Warning';
import { OrderedMap } from 'immutable';
import * as React from 'react';
import { PluginCommands } from '../../mol-plugin/commands';
@@ -26,13 +15,13 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { urlCombine } from '../../mol-util/url';
import { PluginUIComponent, PurePluginUIComponent } from '../base';
import { Button, ExpandGroup, IconButton, SectionHeader } from '../controls/common';
import { Icon } from '../controls/icons';
import { Icon, SaveOutlinedSvg, GetAppSvg, OpenInBrowserSvg, WarningSvg, DeleteOutlinedSvg, AddSvg, ArrowUpwardSvg, SwapHorizSvg, ArrowDownwardSvg, RefreshSvg, CloudUploadSvg } from '../controls/icons';
import { ParameterControls } from '../controls/parameters';
export class StateSnapshots extends PluginUIComponent<{}> {
render() {
return <div>
<SectionHeader icon={SaveOutlined} title='Plugin State' />
<SectionHeader icon={SaveOutlinedSvg} title='Plugin State' />
<div style={{ marginBottom: '10px' }}>
<ExpandGroup header='Save Options' initiallyExpanded={false}>
@@ -76,18 +65,18 @@ export class StateExportImportControls extends PluginUIComponent<{ onAction?: ()
render() {
return <>
<div className='msp-flex-row'>
<Button icon={GetApp} onClick={this.downloadToFileJson} title='Save the state description. Input data are loaded using the provided sources. Does not work if local files are used as input.'>
<Button icon={GetAppSvg} onClick={this.downloadToFileJson} title='Save the state description. Input data are loaded using the provided sources. Does not work if local files are used as input.'>
State
</Button>
<Button icon={GetApp} onClick={this.downloadToFileZip} title='Save the state including the input data.'>
<Button icon={GetAppSvg} onClick={this.downloadToFileZip} title='Save the state including the input data.'>
Session
</Button>
<div className='msp-btn msp-btn-block msp-btn-action msp-loader-msp-btn-file'>
<Icon svg={OpenInBrowser} inline /> Open <input onChange={this.open} type='file' multiple={false} accept='.molx,.molj' />
<Icon svg={OpenInBrowserSvg} inline /> Open <input onChange={this.open} type='file' multiple={false} accept='.molx,.molj' />
</div>
</div>
<div className='msp-help-text' style={{ padding: '10px'}}>
<Icon svg={Warning} /> This is an experimental feature and stored states/sessions might not be openable in a future version.
<Icon svg={WarningSvg} /> This is an experimental feature and stored states/sessions might not be openable in a future version.
</div>
</>;
}
@@ -103,7 +92,7 @@ export class LocalStateSnapshotParams extends PluginUIComponent {
}
}
class LocalStateSnapshots extends PluginUIComponent<
export class LocalStateSnapshots extends PluginUIComponent<
{},
{ params: PD.Values<typeof LocalStateSnapshots.Params> }> {
state = { params: PD.getDefaultValues(LocalStateSnapshots.Params) };
@@ -134,14 +123,14 @@ class LocalStateSnapshots extends PluginUIComponent<
return <div>
<ParameterControls params={LocalStateSnapshots.Params} values={this.state.params} onEnter={this.add} onChangeValues={this.updateParams} />
<div className='msp-flex-row'>
<IconButton onClick={this.clear} svg={DeleteOutlined} title='Remove All' />
<Button onClick={this.add} icon={Add} style={{ textAlign: 'right' }} commit>Add</Button>
<IconButton onClick={this.clear} svg={DeleteOutlinedSvg} title='Remove All' />
<Button onClick={this.add} icon={AddSvg} style={{ textAlign: 'right' }} commit>Add</Button>
</div>
</div>;
}
}
class LocalStateSnapshotList extends PluginUIComponent<{}, {}> {
export class LocalStateSnapshotList extends PluginUIComponent<{}, {}> {
componentDidMount() {
this.subscribe(this.plugin.managers.snapshot.events.changed, () => this.forceUpdate());
}
@@ -186,10 +175,10 @@ class LocalStateSnapshotList extends PluginUIComponent<{}, {}> {
{`${e!.snapshot.durationInMs ? formatTimespan(e!.snapshot.durationInMs, false) + `${e!.description ? ', ' : ''}` : ''}${e!.description ? e!.description : ''}`}
</small>
</Button>
<IconButton svg={ArrowUpward} data-id={e!.snapshot.id} title='Move Up' onClick={this.moveUp} flex='20px' />
<IconButton svg={ArrowDownward} data-id={e!.snapshot.id} title='Move Down' onClick={this.moveDown} flex='20px' />
<IconButton svg={SwapHoriz} data-id={e!.snapshot.id} title='Replace' onClick={this.replace} flex='20px' />
<IconButton svg={DeleteOutlined} data-id={e!.snapshot.id} title='Remove' onClick={this.remove} flex='20px' />
<IconButton svg={ArrowUpwardSvg} data-id={e!.snapshot.id} title='Move Up' onClick={this.moveUp} flex='20px' />
<IconButton svg={ArrowDownwardSvg} data-id={e!.snapshot.id} title='Move Down' onClick={this.moveDown} flex='20px' />
<IconButton svg={SwapHorizSvg} data-id={e!.snapshot.id} title='Replace' onClick={this.replace} flex='20px' />
<IconButton svg={DeleteOutlinedSvg} data-id={e!.snapshot.id} title='Remove' onClick={this.remove} flex='20px' />
</li>)}
</ul>;
}
@@ -317,8 +306,8 @@ export class RemoteStateSnapshots extends PluginUIComponent<
this.setState({ params: { ...this.state.params, [p.name]: p.value } } as any);
}} isDisabled={this.state.isBusy} />
<div className='msp-flex-row'>
<IconButton onClick={this.refresh} disabled={this.state.isBusy} svg={Refresh} />
<Button icon={CloudUpload} onClick={this.upload} disabled={this.state.isBusy} commit>Upload</Button>
<IconButton onClick={this.refresh} disabled={this.state.isBusy} svg={RefreshSvg} />
<Button icon={CloudUploadSvg} onClick={this.upload} disabled={this.state.isBusy} commit>Upload</Button>
</div>
</>}
@@ -330,7 +319,7 @@ export class RemoteStateSnapshots extends PluginUIComponent<
this.setState({ params: { ...this.state.params, [p.name]: p.value } } as any);
}} isDisabled={this.state.isBusy} />
<div className='msp-flex-row'>
<Button onClick={this.refresh} disabled={this.state.isBusy} icon={Refresh}>Refresh</Button>
<Button onClick={this.refresh} disabled={this.state.isBusy} icon={RefreshSvg}>Refresh</Button>
</div>
</div>}
</>;
@@ -361,7 +350,7 @@ class RemoteStateSnapshotList extends PurePluginUIComponent<
disabled={this.props.isBusy} onContextMenu={this.open} title='Click to download, right-click to open in a new tab.'>
{e!.name || new Date(e!.timestamp).toLocaleString()} <small>{e!.description}</small>
</Button>
{!e!.isSticky && this.props.remove && <IconButton svg={DeleteOutlined} data-id={e!.id} title='Remove' onClick={this.props.remove} disabled={this.props.isBusy} small />}
{!e!.isSticky && this.props.remove && <IconButton svg={DeleteOutlinedSvg} data-id={e!.id} title='Remove' onClick={this.props.remove} disabled={this.props.isBusy} small />}
</li>)}
</ul>;
}

View File

@@ -4,13 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import ArrowRight from '@material-ui/icons/ArrowRight';
import Close from '@material-ui/icons/Close';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import HomeOutlined from '@material-ui/icons/HomeOutlined';
import VisibilityOffOutlined from '@material-ui/icons/VisibilityOffOutlined';
import VisibilityOutlined from '@material-ui/icons/VisibilityOutlined';
import * as React from 'react';
import { debounceTime, filter } from 'rxjs/operators';
import { PluginStateObject } from '../../mol-plugin-state/objects';
@@ -20,7 +13,7 @@ import { StateTreeSpine } from '../../mol-state/tree/spine';
import { PluginUIComponent, _Props, _State } from '../base';
import { ActionMenu } from '../controls/action-menu';
import { Button, ControlGroup, IconButton } from '../controls/common';
import { Icon } from '../controls/icons';
import { Icon, HomeOutlinedSvg, ArrowRightSvg, ArrowDropDownSvg, DeleteOutlinedSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, CloseSvg } from '../controls/icons';
import { ApplyActionControl } from './apply-action';
import { UpdateTransformControl } from './update-transform';
@@ -50,7 +43,7 @@ export class StateTree extends PluginUIComponent<{ state: State }, { showActions
if (this.state.showActions) {
return <div style={{ margin: '10px', cursor: 'default' }}>
<p>Nothing to see here yet.</p>
<p>Structures and Volumes can be loaded from the <Icon svg={HomeOutlined} /> tab.</p>
<p>Structures and Volumes can be loaded from the <Icon svg={HomeOutlinedSvg} /> tab.</p>
</div>;
}
return <StateTreeNode cell={this.props.state.cells.get(ref)!} depth={0} />;
@@ -306,9 +299,9 @@ class StateTreeNodeLabel extends PluginUIComponent<{ cell: StateObjectCell, dept
const children = cell.parent!.tree.children.get(this.ref);
const cellState = cell.state;
const expand = <IconButton svg={cellState.isCollapsed ? ArrowRight : ArrowDropDown} flex='20px' disabled={disabled} onClick={this.toggleExpanded} transparent className='msp-no-hover-outline' style={{ visibility: children.size > 0 ? 'visible' : 'hidden' }} />;
const remove = !cell.state.isLocked ? <IconButton svg={DeleteOutlined} onClick={this.remove} disabled={disabled} small toggleState={false} /> : void 0;
const visibility = <IconButton svg={cellState.isHidden ? VisibilityOffOutlined : VisibilityOutlined} toggleState={false} disabled={disabled} small onClick={this.toggleVisible} />;
const expand = <IconButton svg={cellState.isCollapsed ? ArrowRightSvg : ArrowDropDownSvg} flex='20px' disabled={disabled} onClick={this.toggleExpanded} transparent className='msp-no-hover-outline' style={{ visibility: children.size > 0 ? 'visible' : 'hidden' }} />;
const remove = !cell.state.isLocked ? <IconButton svg={DeleteOutlinedSvg} onClick={this.remove} disabled={disabled} small toggleState={false} /> : void 0;
const visibility = <IconButton svg={cellState.isHidden ? VisibilityOffOutlinedSvg : VisibilityOutlinedSvg} toggleState={false} disabled={disabled} small onClick={this.toggleVisible} />;
const marginStyle: React.CSSProperties = {
marginLeft: `${this.props.depth * 8}px`
@@ -326,7 +319,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ cell: StateObjectCell, dept
if (this.state.action === 'apply' && this.state.currentAction) {
return <div style={{ marginBottom: '1px' }}>
{row}
<ControlGroup header={`Apply ${this.state.currentAction.definition.display.name}`} initialExpanded={true} hideExpander={true} hideOffset={false} onHeaderClick={this.hideApply} topRightIcon={Close} headerLeftMargin={`${this.props.depth * 8 + 21}px`}>
<ControlGroup header={`Apply ${this.state.currentAction.definition.display.name}`} initialExpanded={true} hideExpander={true} hideOffset={false} onHeaderClick={this.hideApply} topRightIcon={CloseSvg} headerLeftMargin={`${this.props.depth * 8 + 21}px`}>
<ApplyActionControl onApply={this.hideApply} state={this.props.cell.parent!} action={this.state.currentAction} nodeRef={this.props.cell.transform.ref} hideHeader noMargin />
</ControlGroup>
</div>;

View File

@@ -4,15 +4,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Add from '@material-ui/icons/Add';
import BookmarksOutlined from '@material-ui/icons/BookmarksOutlined';
import Delete from '@material-ui/icons/Delete';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import MoreHoriz from '@material-ui/icons/MoreHoriz';
import Restore from '@material-ui/icons/Restore';
import Tune from '@material-ui/icons/Tune';
import VisibilityOffOutlined from '@material-ui/icons/VisibilityOffOutlined';
import VisibilityOutlined from '@material-ui/icons/VisibilityOutlined';
import * as React from 'react';
import { getStructureThemeTypes } from '../../mol-plugin-state/helpers/structure-representation-params';
import { StructureComponentManager } from '../../mol-plugin-state/manager/structure/component';
@@ -24,7 +15,7 @@ import { ParamDefinition } from '../../mol-util/param-definition';
import { CollapsableControls, CollapsableState, PurePluginUIComponent } from '../base';
import { ActionMenu } from '../controls/action-menu';
import { Button, ExpandGroup, IconButton, ToggleButton } from '../controls/common';
import { CubeSvg, IntersectSvg, SetSvg, SubtractSvg, UnionSvg } from '../controls/icons';
import { CubeOutlineSvg, IntersectSvg, SetSvg, SubtractSvg, UnionSvg, BookmarksOutlinedSvg, AddSvg, TuneSvg, RestoreSvg, DeleteSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, DeleteOutlinedSvg, MoreHorizSvg } from '../controls/icons';
import { ParameterControls } from '../controls/parameters';
import { UpdateTransformControl } from '../state/update-transform';
import { GenericEntryListControls } from './generic';
@@ -39,7 +30,7 @@ export class StructureComponentControls extends CollapsableControls<{}, Structur
header: 'Representation',
isCollapsed: false,
isDisabled: false,
brand: { accent: 'blue', svg: CubeSvg }
brand: { accent: 'blue', svg: CubeOutlineSvg }
};
}
@@ -130,10 +121,10 @@ class ComponentEditorControls extends PurePluginUIComponent<{}, ComponentEditorC
: 'Some mistakes of the past can be undone.';
return <>
<div className='msp-flex-row'>
<ToggleButton icon={BookmarksOutlined} label='Preset' title='Apply a representation preset for the current structure(s).' toggle={this.togglePreset} isSelected={this.state.action === 'preset'} disabled={this.isDisabled} />
<ToggleButton icon={Add} label='Add' title='Add a new representation component for a selection.' toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
<ToggleButton icon={Tune} label='' title='Options that are applied to all applicable representations.' style={{ flex: '0 0 40px', padding: 0 }} toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.isDisabled} />
<IconButton svg={Restore} className='msp-flex-item' flex='40px' onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} title={undoTitle} />
<ToggleButton icon={BookmarksOutlinedSvg} label='Preset' title='Apply a representation preset for the current structure(s).' toggle={this.togglePreset} isSelected={this.state.action === 'preset'} disabled={this.isDisabled} />
<ToggleButton icon={AddSvg} label='Add' title='Add a new representation component for a selection.' toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.isDisabled} />
<ToggleButton icon={TuneSvg} label='' title='Options that are applied to all applicable representations.' style={{ flex: '0 0 40px', padding: 0 }} toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.isDisabled} />
<IconButton svg={RestoreSvg} className='msp-flex-item' flex='40px' onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} title={undoTitle} />
</div>
{this.state.action === 'preset' && this.presetControls}
{this.state.action === 'add' && <div className='msp-control-offset'>
@@ -179,7 +170,7 @@ export class AddComponentControls extends PurePluginUIComponent<AddComponentCont
render() {
return <>
<ParameterControls params={this.state.params} values={this.state.values} onChangeValues={this.paramsChanged} />
<Button icon={Add} title='Use Selection and optional Representation to create a new Component.' className='msp-btn-commit msp-btn-commit-on' onClick={this.apply} style={{ marginTop: '1px' }}>
<Button icon={AddSvg} title='Use Selection and optional Representation to create a new Component.' className='msp-btn-commit msp-btn-commit-on' onClick={this.apply} style={{ marginTop: '1px' }}>
Create Component
</Button>
</>;
@@ -286,7 +277,7 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
get removeActions(): ActionMenu.Items {
const ret = [
ActionMenu.Item('Remove', () => this.plugin.managers.structure.hierarchy.remove(this.props.group, true), { icon: Delete })
ActionMenu.Item('Remove', () => this.plugin.managers.structure.hierarchy.remove(this.props.group, true), { icon: DeleteSvg })
];
const reprs = this.pivot.representations;
@@ -294,7 +285,7 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
return ret;
}
ret.push(ActionMenu.Item(`Remove Representation${reprs.length > 1 ? 's' : ''}`, () => this.plugin.managers.structure.component.removeRepresentations(this.props.group), { icon: Delete }));
ret.push(ActionMenu.Item(`Remove Representation${reprs.length > 1 ? 's' : ''}`, () => this.plugin.managers.structure.component.removeRepresentations(this.props.group), { icon: DeleteSvg }));
return ret;
}
@@ -357,9 +348,9 @@ class StructureComponentGroup extends PurePluginUIComponent<{ group: StructureCo
{label}
<small className='msp-25-lower-contrast-text' style={{ float: 'right' }}>{reprLabel}</small>
</Button>
<IconButton svg={cell.state.isHidden ? VisibilityOffOutlined : VisibilityOutlined} toggleState={false} onClick={this.toggleVisible} title={`${cell.state.isHidden ? 'Show' : 'Hide'} component`} small className='msp-form-control' flex />
<IconButton svg={DeleteOutlined} onClick={this.toggleRemove} title='Remove' small toggleState={this.state.action === 'remove'} className='msp-form-control' flex />
<IconButton svg={MoreHoriz} onClick={this.toggleAction} title='Actions' toggleState={this.state.action === 'action'} className='msp-form-control' flex />
<IconButton svg={cell.state.isHidden ? VisibilityOffOutlinedSvg : VisibilityOutlinedSvg} toggleState={false} onClick={this.toggleVisible} title={`${cell.state.isHidden ? 'Show' : 'Hide'} component`} small className='msp-form-control' flex />
<IconButton svg={DeleteOutlinedSvg} onClick={this.toggleRemove} title='Remove' small toggleState={this.state.action === 'remove'} className='msp-form-control' flex />
<IconButton svg={MoreHorizSvg} onClick={this.toggleAction} title='Actions' toggleState={this.state.action === 'action'} className='msp-form-control' flex />
</div>
{this.state.action === 'remove' && <div style={{ marginBottom: '6px' }}>
<ActionMenu items={this.removeActions} onSelect={this.selectRemoveAction} />
@@ -398,10 +389,10 @@ class StructureRepresentationEntry extends PurePluginUIComponent<{ group: Struct
{repr.parent && <ExpandGroup header={`${repr.obj?.label || ''} Representation`} noOffset>
<UpdateTransformControl state={repr.parent} transform={repr.transform} customHeader='none' customUpdate={this.update} noMargin />
</ExpandGroup>}
<IconButton svg={DeleteOutlined} onClick={this.remove} title='Remove' small className='msp-default-bg' toggleState={false} style={{
<IconButton svg={DeleteOutlinedSvg} onClick={this.remove} title='Remove' small className='msp-default-bg' toggleState={false} style={{
position: 'absolute', top: 0, right: '32px', lineHeight: '24px', height: '24px', textAlign: 'right', width: '44px', paddingRight: '6px', background: 'none'
}} />
<IconButton svg={this.props.representation.cell.state.isHidden ? VisibilityOffOutlined : VisibilityOutlined} toggleState={false} onClick={this.toggleVisible} title='Toggle Visibility' small className='msp-default-bg' style={{
<IconButton svg={this.props.representation.cell.state.isHidden ? VisibilityOffOutlinedSvg : VisibilityOutlinedSvg} toggleState={false} onClick={this.toggleVisible} title='Toggle Visibility' small className='msp-default-bg' style={{
position: 'absolute', top: 0, right: 0, lineHeight: '24px', height: '24px', textAlign: 'right', width: '32px', paddingRight: '6px', background: 'none'
}} />
</div>;

View File

@@ -4,8 +4,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import CenterFocusStrong from '@material-ui/icons/CenterFocusStrong';
import CancelOutlined from '@material-ui/icons/CancelOutlined';
import * as React from 'react';
import { OrderedSet, SortedArray } from '../../mol-data/int';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
@@ -21,6 +19,7 @@ import { memoizeLatest } from '../../mol-util/memoize';
import { PluginUIComponent } from '../base';
import { ActionMenu } from '../controls/action-menu';
import { Button, IconButton, ToggleButton } from '../controls/common';
import { CancelOutlinedSvg, CenterFocusStrongSvg } from '../controls/icons';
interface StructureFocusControlsState {
isBusy: boolean
@@ -238,8 +237,8 @@ export class StructureFocusControls extends PluginUIComponent<{}, StructureFocus
style={{ textAlignLast: current ? 'left' : void 0 }}>
{label}
</Button>
{current && <IconButton svg={CancelOutlined} onClick={this.clear} title='Clear' className='msp-form-control' flex disabled={this.isDisabled} />}
<ToggleButton icon={CenterFocusStrong} title='Select a focus target to center on an show its surroundings. Hold shift to focus on multiple targets.' toggle={this.toggleAction} isSelected={this.state.showAction} disabled={this.isDisabled} style={{ flex: '0 0 40px', padding: 0 }} />
{current && <IconButton svg={CancelOutlinedSvg} onClick={this.clear} title='Clear' className='msp-form-control' flex disabled={this.isDisabled} />}
<ToggleButton icon={CenterFocusStrongSvg} title='Select a focus target to center on an show its surroundings. Hold shift to focus on multiple targets.' toggle={this.toggleAction} isSelected={this.state.showAction} disabled={this.isDisabled} style={{ flex: '0 0 40px', padding: 0 }} />
</div>
{this.state.showAction && <ActionMenu items={this.actionItems} onSelect={this.selectAction} />}
</>;

View File

@@ -5,9 +5,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import MoreHoriz from '@material-ui/icons/MoreHoriz';
import VisibilityOutlined from '@material-ui/icons/VisibilityOutlined';
import VisibilityOffOutlined from '@material-ui/icons/VisibilityOffOutlined';
import * as React from 'react';
import { StructureHierarchyRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
import { PluginCommands } from '../../mol-plugin/commands';
@@ -15,6 +12,7 @@ import { State } from '../../mol-state';
import { PurePluginUIComponent } from '../base';
import { IconButton } from '../controls/common';
import { UpdateTransformControl } from '../state/update-transform';
import { VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, MoreHorizSvg } from '../controls/icons';
export class GenericEntryListControls extends PurePluginUIComponent {
get current() {
@@ -143,8 +141,8 @@ export class GenericEntry<T extends StructureHierarchyRef> extends PurePluginUIC
<button className='msp-form-control msp-control-button-label' title={`${label}. Click to focus.`} onClick={this.focus} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} style={{ textAlign: 'left' }}>
{label} <small>{description}</small>
</button>
<IconButton svg={pivot.cell.state.isHidden ? VisibilityOffOutlined : VisibilityOutlined} toggleState={false} className='msp-form-control' onClick={this.toggleVisibility} title={`${pivot.cell.state.isHidden ? 'Show' : 'Hide'}`} small flex />
{refs.length === 1 && <IconButton svg={MoreHoriz} className='msp-form-control' onClick={this.toggleOptions} title='Options' toggleState={this.state.showOptions} flex />}
<IconButton svg={pivot.cell.state.isHidden ? VisibilityOffOutlinedSvg : VisibilityOutlinedSvg} toggleState={false} className='msp-form-control' onClick={this.toggleVisibility} title={`${pivot.cell.state.isHidden ? 'Show' : 'Hide'}`} small flex />
{refs.length === 1 && <IconButton svg={MoreHorizSvg} className='msp-form-control' onClick={this.toggleOptions} title='Options' toggleState={this.state.showOptions} flex />}
</div>
{(refs.length === 1 && this.state.showOptions && pivot.cell.parent) && <>
<div className='msp-control-offset'>

View File

@@ -5,15 +5,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Add from '@material-ui/icons/Add';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import ArrowUpward from '@material-ui/icons/ArrowUpward';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import HelpOutline from '@material-ui/icons/HelpOutline';
import MoreHoriz from '@material-ui/icons/MoreHoriz';
import Tune from '@material-ui/icons/Tune';
import VisibilityOffOutlined from '@material-ui/icons/VisibilityOffOutlined';
import VisibilityOutlined from '@material-ui/icons/VisibilityOutlined';
import * as React from 'react';
import { Loci } from '../../mol-model/loci';
import { StructureElement } from '../../mol-model/structure';
@@ -26,7 +17,7 @@ import { FiniteArray } from '../../mol-util/type-helpers';
import { CollapsableControls, PurePluginUIComponent } from '../base';
import { ActionMenu } from '../controls/action-menu';
import { Button, ExpandGroup, IconButton, ToggleButton } from '../controls/common';
import { Icon, RulerSvg, SetSvg } from '../controls/icons';
import { Icon, PencilRulerSvg, SetSvg, ArrowUpwardSvg, ArrowDownwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, AddSvg, TuneSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, MoreHorizSvg } from '../controls/icons';
import { ParameterControls } from '../controls/parameters';
import { UpdateTransformControl } from '../state/update-transform';
import { ToggleSelectionModeButton } from './selection';
@@ -38,7 +29,7 @@ export class StructureMeasurementsControls extends CollapsableControls {
return {
isCollapsed: false,
header: 'Measurements',
brand: { accent: 'gray' as const, svg: RulerSvg }
brand: { accent: 'gray' as const, svg: PencilRulerSvg }
};
}
@@ -152,9 +143,9 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={this.plugin.managers.interactivity.lociHighlights.clearHighlights}>
{idx}. <span dangerouslySetInnerHTML={{ __html: e.label }} />
</Button>
{history.length > 1 && <IconButton svg={ArrowUpward} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
{history.length > 1 && <IconButton svg={ArrowDownward} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'down')} flex='20px' title={'Move down'} />}
<IconButton svg={DeleteOutlined} small={true} className='msp-form-control' onClick={() => this.plugin.managers.structure.selection.modifyHistory(e, 'remove')} flex title={'Remove'} />
{history.length > 1 && <IconButton svg={ArrowUpwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
{history.length > 1 && <IconButton svg={ArrowDownwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'down')} flex='20px' title={'Move down'} />}
<IconButton svg={DeleteOutlinedSvg} small={true} className='msp-form-control' onClick={() => this.plugin.managers.structure.selection.modifyHistory(e, 'remove')} flex title={'Remove'} />
</div>;
}
@@ -172,7 +163,7 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
{entries}
</div>}
{entries.length === 0 && <div className='msp-control-offset msp-help-text'>
<div className='msp-help-description'><Icon svg={HelpOutline} inline />Add one or more selections (toggle <ToggleSelectionModeButton inline /> mode)</div>
<div className='msp-help-description'><Icon svg={HelpOutlineSvg} inline />Add one or more selections (toggle <ToggleSelectionModeButton inline /> mode)</div>
</div>}
</>;
}
@@ -180,8 +171,8 @@ export class MeasurementControls extends PurePluginUIComponent<{}, { isBusy: boo
render() {
return <>
<div className='msp-flex-row'>
<ToggleButton icon={Add} label='Add' toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.state.isBusy} className='msp-btn-apply-simple' />
<ToggleButton icon={Tune} label='' title='Options' toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.state.isBusy} style={{ flex: '0 0 40px', padding: 0 }} />
<ToggleButton icon={AddSvg} label='Add' toggle={this.toggleAdd} isSelected={this.state.action === 'add'} disabled={this.state.isBusy} className='msp-btn-apply-simple' />
<ToggleButton icon={TuneSvg} label='' title='Options' toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.state.isBusy} style={{ flex: '0 0 40px', padding: 0 }} />
</div>
{this.state.action === 'add' && this.add()}
{this.state.action === 'options' && <MeasurementsOptions />}
@@ -297,9 +288,9 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
<button className='msp-form-control msp-control-button-label msp-no-overflow' title='Click to focus. Hover to highlight.' onClick={this.focus} style={{ width: 'auto', textAlign: 'left' }}>
<span dangerouslySetInnerHTML={{ __html: this.label }} />
</button>
<IconButton svg={cell.state.isHidden ? VisibilityOffOutlined : VisibilityOutlined} toggleState={false} small className='msp-form-control' onClick={this.toggleVisibility} flex title={cell.state.isHidden ? 'Show' : 'Hide'} />
<IconButton svg={DeleteOutlined} small className='msp-form-control' onClick={this.delete} flex title='Delete' toggleState={false} />
<IconButton svg={MoreHoriz} className='msp-form-control' onClick={this.toggleUpdate} flex title='Actions' toggleState={this.state.showUpdate} />
<IconButton svg={cell.state.isHidden ? VisibilityOffOutlinedSvg : VisibilityOutlinedSvg} toggleState={false} small className='msp-form-control' onClick={this.toggleVisibility} flex title={cell.state.isHidden ? 'Show' : 'Hide'} />
<IconButton svg={DeleteOutlinedSvg} small className='msp-form-control' onClick={this.delete} flex title='Delete' toggleState={false} />
<IconButton svg={MoreHorizSvg} className='msp-form-control' onClick={this.toggleUpdate} flex title='Actions' toggleState={this.state.showUpdate} />
</div>
{this.state.showUpdate && cell.parent && <>
<div className='msp-accent-offset'>

View File

@@ -5,12 +5,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Close from '@material-ui/icons/Close';
import CancelOutlined from '@material-ui/icons/CancelOutlined';
import Brush from '@material-ui/icons/Brush';
import Restore from '@material-ui/icons/Restore';
import Remove from '@material-ui/icons/Remove';
import HelpOutline from '@material-ui/icons/HelpOutline';
import * as React from 'react';
import { StructureSelectionQueries, StructureSelectionQuery, getNonStandardResidueQueries, getElementQueries, getPolymerAndBranchedEntityQueries } from '../../mol-plugin-state/helpers/structure-selection-query';
import { InteractivityManager } from '../../mol-plugin-state/manager/interactivity';
@@ -24,7 +18,7 @@ import { PluginUIComponent, PurePluginUIComponent } from '../base';
import { ActionMenu } from '../controls/action-menu';
import { Button, ControlGroup, IconButton, ToggleButton } from '../controls/common';
import { ParameterControls, ParamOnChange, PureSelectControl } from '../controls/parameters';
import { UnionSvg, SubtractSvg, IntersectSvg, SetSvg, CubeSvg, Icon, CursorSvg } from '../controls/icons';
import { UnionSvg, SubtractSvg, IntersectSvg, SetSvg, CubeOutlineSvg, Icon, SelectionModeSvg, RemoveSvg, RestoreSvg, HelpOutlineSvg, CancelOutlinedSvg, BrushSvg, CloseSvg } from '../controls/icons';
import { AddComponentControls } from './components';
import Structure from '../../mol-model/structure/structure/structure';
import { ViewportHelpContent, HelpGroup, HelpText } from '../viewport/help';
@@ -45,11 +39,10 @@ export class ToggleSelectionModeButton extends PurePluginUIComponent<{ inline?:
const style = this.props.inline
? { background: 'transparent', width: 'auto', height: 'auto', lineHeight: 'unset' }
: { background: 'transparent' };
return <IconButton svg={CursorSvg} onClick={this._toggleSelMode} title={'Toggle Selection Mode'} style={style} toggleState={this.plugin.selectionMode} />;
return <IconButton svg={SelectionModeSvg} onClick={this._toggleSelMode} title={'Toggle Selection Mode'} style={style} toggleState={this.plugin.selectionMode} />;
}
}
const StructureSelectionParams = {
granularity: InteractivityManager.Params.granularity,
};
@@ -59,7 +52,7 @@ interface StructureSelectionActionsControlsState {
isBusy: boolean,
canUndo: boolean,
action?: StructureSelectionModifier | 'color' | 'add-repr' | 'help'
action?: StructureSelectionModifier | 'theme' | 'add-repr' | 'help'
}
const ActionHeader = new Map<StructureSelectionModifier, string>([
@@ -165,7 +158,7 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
toggleRemove = this.showAction('remove')
toggleIntersect = this.showAction('intersect')
toggleSet = this.showAction('set')
toggleColor = this.showAction('color')
toggleTheme = this.showAction('theme')
toggleAddRepr = this.showAction('add-repr')
toggleHelp = this.showAction('help')
@@ -202,34 +195,34 @@ export class StructureSelectionActionsControls extends PluginUIComponent<{}, Str
<ToggleButton icon={IntersectSvg} title={`${ActionHeader.get('intersect')}. Hold shift key to keep menu open.`} toggle={this.toggleIntersect} isSelected={this.state.action === 'intersect'} disabled={this.isDisabled} />
<ToggleButton icon={SetSvg} title={`${ActionHeader.get('set')}. Hold shift key to keep menu open.`} toggle={this.toggleSet} isSelected={this.state.action === 'set'} disabled={this.isDisabled} />
<ToggleButton icon={Brush} title='Color Selection' toggle={this.toggleColor} isSelected={this.state.action === 'color'} disabled={this.isDisabled} style={{ marginLeft: '10px' }} />
<ToggleButton icon={CubeSvg} title='Create Representation of Selection' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
<IconButton svg={Remove} title='Subtract Selection from Representations' onClick={this.subtract} disabled={this.isDisabled} />
<IconButton svg={Restore} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} title={undoTitle} />
<ToggleButton icon={BrushSvg} title='Apply Theme to Selection' toggle={this.toggleTheme} isSelected={this.state.action === 'theme'} disabled={this.isDisabled} style={{ marginLeft: '10px' }} />
<ToggleButton icon={CubeOutlineSvg} title='Create Representation of Selection' toggle={this.toggleAddRepr} isSelected={this.state.action === 'add-repr'} disabled={this.isDisabled} />
<IconButton svg={RemoveSvg} title='Subtract Selection from Representations' onClick={this.subtract} disabled={this.isDisabled} />
<IconButton svg={RestoreSvg} onClick={this.undo} disabled={!this.state.canUndo || this.isDisabled} title={undoTitle} />
<ToggleButton icon={HelpOutline} title='Show/hide help' toggle={this.toggleHelp} style={{ marginLeft: '10px' }} isSelected={this.state.action === 'help'} />
<IconButton svg={CancelOutlined} title='Turn selection mode off' onClick={this.turnOff} />
<ToggleButton icon={HelpOutlineSvg} title='Show/hide help' toggle={this.toggleHelp} style={{ marginLeft: '10px' }} isSelected={this.state.action === 'help'} />
<IconButton svg={CancelOutlinedSvg} title='Turn selection mode off' onClick={this.turnOff} />
</div>
{(this.state.action && this.state.action !== 'color' && this.state.action !== 'add-repr' && this.state.action !== 'help') && <div className='msp-selection-viewport-controls-actions'>
{(this.state.action && this.state.action !== 'theme' && this.state.action !== 'add-repr' && this.state.action !== 'help') && <div className='msp-selection-viewport-controls-actions'>
<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' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleColor} topRightIcon={Close}>
<ApplyColorControls onApply={this.toggleColor} />
{this.state.action === 'theme' && <div className='msp-selection-viewport-controls-actions'>
<ControlGroup header='Theme' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleTheme} topRightIcon={CloseSvg}>
<ApplyThemeControls onApply={this.toggleTheme} />
</ControlGroup>
</div>}
{this.state.action === 'add-repr' && <div className='msp-selection-viewport-controls-actions'>
<ControlGroup header='Add Representation' title='Click to close.' 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={CloseSvg}>
<AddComponentControls onApply={this.toggleAddRepr} forSelection />
</ControlGroup>
</div>}
{this.state.action === 'help' && <div className='msp-selection-viewport-controls-actions'>
<ControlGroup header='Help' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleHelp} topRightIcon={Close} maxHeight='300px'>
<ControlGroup header='Help' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleHelp} topRightIcon={CloseSvg} maxHeight='300px'>
<HelpGroup header='Selection Operations'>
<HelpText>Use <Icon svg={UnionSvg} inline /> <Icon svg={SubtractSvg} inline /> <Icon svg={IntersectSvg} inline /> <Icon svg={SetSvg} inline /> to modify the selection.</HelpText>
</HelpGroup>
<HelpGroup header='Representation Operations'>
<HelpText>Use <Icon svg={Brush} inline /> <Icon svg={CubeSvg} inline /> <Icon svg={Remove} inline /> <Icon svg={Restore} inline /> to color, create selection/representation, subtract it, or undo actions.</HelpText>
<HelpText>Use <Icon svg={BrushSvg} inline /> <Icon svg={CubeOutlineSvg} inline /> <Icon svg={RemoveSvg} inline /> <Icon svg={RestoreSvg} inline /> to color, create selection/representation, subtract it, or undo actions.</HelpText>
</HelpGroup>
<ViewportHelpContent selectOnly={true} />
</ControlGroup>
@@ -305,28 +298,28 @@ export class StructureSelectionStatsControls extends PluginUIComponent<{ hideOnE
style={{ textAlignLast: !empty ? 'left' : void 0 }}>
{this.stats}
</Button>
{!empty && <IconButton svg={CancelOutlined} onClick={this.clear} title='Clear' className='msp-form-control' flex />}
{!empty && <IconButton svg={CancelOutlinedSvg} onClick={this.clear} title='Clear' className='msp-form-control' flex />}
</div>
</>;
}
}
interface ApplyColorControlsState {
values: StructureComponentManager.ColorParams
values: StructureComponentManager.ThemeParams
}
interface ApplyColorControlsProps {
onApply?: () => void
}
class ApplyColorControls extends PurePluginUIComponent<ApplyColorControlsProps, ApplyColorControlsState> {
_params = memoizeLatest((pivot: StructureRef | undefined) => StructureComponentManager.getColorParams(this.plugin, pivot));
class ApplyThemeControls extends PurePluginUIComponent<ApplyColorControlsProps, ApplyColorControlsState> {
_params = memoizeLatest((pivot: StructureRef | undefined) => StructureComponentManager.getThemeParams(this.plugin, pivot));
get params() { return this._params(this.plugin.managers.structure.component.pivotStructure); }
state = { values: ParamDefinition.getDefaultValues(this.params) };
apply = () => {
this.plugin.managers.structure.component.applyColor(this.state.values);
this.plugin.managers.structure.component.applyTheme(this.state.values);
this.props.onApply?.();
}
@@ -335,8 +328,8 @@ class ApplyColorControls extends PurePluginUIComponent<ApplyColorControlsProps,
render() {
return <>
<ParameterControls params={this.params} values={this.state.values} onChangeValues={this.paramsChanged} />
<Button icon={Brush} className='msp-btn-commit msp-btn-commit-on' onClick={this.apply} style={{ marginTop: '1px' }}>
Apply Coloring
<Button icon={BrushSvg} className='msp-btn-commit msp-btn-commit-on' onClick={this.apply} style={{ marginTop: '1px' }}>
Apply Theme
</Button>
</>;
}

View File

@@ -16,8 +16,7 @@ import { StructureFocusControls } from './focus';
import { UpdateTransformControl } from '../state/update-transform';
import { StructureSelectionStatsControls } from './selection';
import { StateSelection } from '../../mol-state';
import BookmarksOutlined from '@material-ui/icons/BookmarksOutlined';
import { MoleculeSvg } from '../controls/icons';
import { MoleculeSvg, BookmarksOutlinedSvg } from '../controls/icons';
interface StructureSourceControlState extends CollapsableState {
isBusy: boolean,
@@ -270,7 +269,7 @@ export class StructureSourceControls extends CollapsableControls<{}, StructureSo
return <>
<div className='msp-flex-row' style={{ marginTop: '1px' }}>
<Button noOverflow flex onClick={this.toggleHierarchy} disabled={disabled} title={label}>{label}</Button>
{presets.length > 0 && <IconButton svg={BookmarksOutlined} className='msp-form-control' flex='40px' onClick={this.togglePreset} title='Apply a structure presets to the current hierarchy.' toggleState={this.state.show === 'presets'} disabled={disabled} />}
{presets.length > 0 && <IconButton svg={BookmarksOutlinedSvg} className='msp-form-control' flex='40px' onClick={this.togglePreset} title='Apply a structure presets to the current hierarchy.' toggleState={this.state.show === 'presets'} disabled={disabled} />}
</div>
{this.state.show === 'hierarchy' && <ActionMenu items={this.hierarchyItems} onSelect={this.selectHierarchy} multiselect />}
{this.state.show === 'presets' && <ActionMenu items={presets} onSelect={this.applyPreset} />}

View File

@@ -5,16 +5,8 @@
*/
import * as React from 'react';
import FlipToFrontIcon from '@material-ui/icons/FlipToFront';
import HelpOutline from '@material-ui/icons/HelpOutline';
import LinearScaleIcon from '@material-ui/icons/LinearScale';
import ScatterPlotIcon from '@material-ui/icons/ScatterPlot';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import ArrowUpward from '@material-ui/icons/ArrowUpward';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import Tune from '@material-ui/icons/Tune';
import { CollapsableControls, PurePluginUIComponent } from '../base';
import { Icon } from '../controls/icons';
import { Icon, ArrowUpwardSvg, ArrowDownwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, TuneSvg, SuperposeAtomsSvg, SuperposeChainsSvg, SuperpositionSvg } from '../controls/icons';
import { Button, ToggleButton, IconButton } from '../controls/common';
import { StructureElement, StructureSelection, QueryContext, Structure, StructureProperties } from '../../mol-model/structure';
import { Mat4 } from '../../mol-math/linear-algebra';
@@ -35,7 +27,7 @@ export class StructureSuperpositionControls extends CollapsableControls {
return {
isCollapsed: false,
header: 'Superposition',
brand: { accent: 'gray' as const, svg: FlipToFrontIcon },
brand: { accent: 'gray' as const, svg: SuperpositionSvg },
isHidden: true
};
}
@@ -115,7 +107,7 @@ export class SuperpositionControls extends PurePluginUIComponent<{}, Superpositi
}
};
// TODO add .insertOrUpdate to StateBuilder?
let b = o
const b = o
? this.plugin.state.data.build().to(o).update(params)
: this.plugin.state.data.build().to(s)
.insert(StateTransforms.Model.TransformStructureConformation, params, { tags: SuperpositionTag });
@@ -203,9 +195,9 @@ export class SuperpositionControls extends PurePluginUIComponent<{}, Superpositi
<Button noOverflow title='Click to focus. Hover to highlight.' onClick={() => this.focusLoci(e.loci)} style={{ width: 'auto', textAlign: 'left' }} onMouseEnter={() => this.highlight(e.loci)} onMouseLeave={this.plugin.managers.interactivity.lociHighlights.clearHighlights}>
{idx}. <span dangerouslySetInnerHTML={{ __html: e.label }} />
</Button>
{history.length > 1 && <IconButton svg={ArrowUpward} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
{history.length > 1 && <IconButton svg={ArrowDownward} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'down')} flex='20px' title={'Move down'} />}
<IconButton svg={DeleteOutlined} small={true} className='msp-form-control' onClick={() => this.plugin.managers.structure.selection.modifyHistory(e, 'remove')} flex title={'Remove'} />
{history.length > 1 && <IconButton svg={ArrowUpwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'up')} flex='20px' title={'Move up'} />}
{history.length > 1 && <IconButton svg={ArrowDownwardSvg} small={true} className='msp-form-control' onClick={() => this.moveHistory(e, 'down')} flex='20px' title={'Move down'} />}
<IconButton svg={DeleteOutlinedSvg} small={true} className='msp-form-control' onClick={() => this.plugin.managers.structure.selection.modifyHistory(e, 'remove')} flex title={'Remove'} />
</div>;
}
@@ -278,7 +270,7 @@ export class SuperpositionControls extends PurePluginUIComponent<{}, Superpositi
{entries.map((e, i) => this.lociEntry(e, i))}
</div>}
{entries.length < 2 && <div className='msp-control-offset msp-help-text'>
<div className='msp-help-description'><Icon svg={HelpOutline} inline />Add 2 or more selections (toggle <ToggleSelectionModeButton inline /> mode) from separate structures. Selections must be limited to single polymer chains or residues therein.</div>
<div className='msp-help-description'><Icon svg={HelpOutlineSvg} inline />Add 2 or more selections (toggle <ToggleSelectionModeButton inline /> mode) from separate structures. Selections must be limited to single polymer chains or residues therein.</div>
</div>}
{entries.length > 1 && <Button title='Superpose structures by selected chains.' className='msp-btn-commit msp-btn-commit-on' onClick={this.superposeChains} style={{ marginTop: '1px' }}>
Superpose
@@ -293,7 +285,7 @@ export class SuperpositionControls extends PurePluginUIComponent<{}, Superpositi
{entries.map((e, i) => this.atomsLociEntry(e, i))}
</div>}
{entries.length < 2 && <div className='msp-control-offset msp-help-text'>
<div className='msp-help-description'><Icon svg={HelpOutline} inline />Add 1 or more selections (toggle <ToggleSelectionModeButton inline /> mode) from
<div className='msp-help-description'><Icon svg={HelpOutlineSvg} inline />Add 1 or more selections (toggle <ToggleSelectionModeButton inline /> mode) from
separate structures. Selections must be limited to single atoms.</div>
</div>}
{entries.length > 1 && <Button title='Superpose structures by selected atoms.' className='msp-btn-commit msp-btn-commit-on' onClick={this.superposeAtoms} style={{ marginTop: '1px' }}>
@@ -309,9 +301,9 @@ export class SuperpositionControls extends PurePluginUIComponent<{}, Superpositi
render() {
return <>
<div className='msp-flex-row'>
<ToggleButton icon={LinearScaleIcon} label='By Chains' toggle={this.toggleByChains} isSelected={this.state.action === 'byChains'} disabled={this.state.isBusy} />
<ToggleButton icon={ScatterPlotIcon} label='By Atoms' toggle={this.toggleByAtoms} isSelected={this.state.action === 'byAtoms'} disabled={this.state.isBusy} />
<ToggleButton icon={Tune} label='' title='Options' toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.state.isBusy} style={{ flex: '0 0 40px', padding: 0 }} />
<ToggleButton icon={SuperposeChainsSvg} label='By Chains' toggle={this.toggleByChains} isSelected={this.state.action === 'byChains'} disabled={this.state.isBusy} />
<ToggleButton icon={SuperposeAtomsSvg} label='By Atoms' toggle={this.toggleByAtoms} isSelected={this.state.action === 'byAtoms'} disabled={this.state.isBusy} />
<ToggleButton icon={TuneSvg} label='' title='Options' toggle={this.toggleOptions} isSelected={this.state.action === 'options'} disabled={this.state.isBusy} style={{ flex: '0 0 40px', padding: 0 }} />
</div>
{this.state.action === 'byChains' && this.addByChains()}
{this.state.action === 'byAtoms' && this.addByAtoms()}

View File

@@ -5,14 +5,6 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import Add from '@material-ui/icons/Add';
import BlurOn from '@material-ui/icons/BlurOn';
import Check from '@material-ui/icons/Check';
import ErrorSvg from '@material-ui/icons/Error';
import DeleteOutlined from '@material-ui/icons/DeleteOutlined';
import MoreHoriz from '@material-ui/icons/MoreHoriz';
import VisibilityOffOutlined from '@material-ui/icons/VisibilityOffOutlined';
import VisibilityOutlined from '@material-ui/icons/VisibilityOutlined';
import * as React from 'react';
import { StructureHierarchyManager } from '../../mol-plugin-state/manager/structure/hierarchy';
import { VolumeHierarchyManager } from '../../mol-plugin-state/manager/volume/hierarchy';
@@ -28,6 +20,7 @@ import { ApplyActionControl } from '../state/apply-action';
import { UpdateTransformControl } from '../state/update-transform';
import { BindingsHelp } from '../viewport/help';
import { PluginCommands } from '../../mol-plugin/commands';
import { BlurOnSvg, ErrorSvg, CheckSvg, AddSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, DeleteOutlinedSvg, MoreHorizSvg } from '../controls/icons';
interface VolumeStreamingControlState extends CollapsableState {
isBusy: boolean
@@ -40,7 +33,7 @@ export class VolumeStreamingControls extends CollapsableControls<{}, VolumeStrea
isCollapsed: false,
isBusy: false,
isHidden: true,
brand: { accent: 'cyan', svg: BlurOn }
brand: { accent: 'cyan', svg: BlurOnSvg }
};
}
@@ -84,7 +77,7 @@ export class VolumeStreamingControls extends CollapsableControls<{}, VolumeStrea
? { header: 'Error enabling', icon: ErrorSvg, title: rootCell.errorText }
: rootCell && rootCell.obj?.data.entries.length === 0
? { header: 'Error enabling', icon: ErrorSvg, title: 'No entry for streaming found' }
: { header: 'Enable', icon: Check, title: 'Enable' };
: { header: 'Enable', icon: CheckSvg, title: 'Enable' };
return <ApplyActionControl state={pivot.cell.parent} action={InitVolumeStreaming} initiallyCollapsed={true} nodeRef={pivot.cell.transform.ref} simpleApply={simpleApply} />;
}
@@ -121,7 +114,7 @@ export class VolumeSourceControls extends CollapsableControls<{}, VolumeSourceCo
isCollapsed: false,
isBusy: false,
isHidden: true,
brand: { accent: 'purple', svg: BlurOn }
brand: { accent: 'purple', svg: BlurOnSvg }
};
}
@@ -199,7 +192,7 @@ export class VolumeSourceControls extends CollapsableControls<{}, VolumeSourceCo
return <>
<div className='msp-flex-row' style={{ marginTop: '1px' }}>
<Button noOverflow flex onClick={this.toggleHierarchy} disabled={disabled} title={label}>{label}</Button>
{!this.isEmpty && <IconButton svg={Add} onClick={this.toggleAddRepr} title='Apply a structure presets to the current hierarchy.' toggleState={this.state.show === 'add-repr'} disabled={disabled} />}
{!this.isEmpty && <IconButton svg={AddSvg} onClick={this.toggleAddRepr} title='Apply a structure presets to the current hierarchy.' toggleState={this.state.show === 'add-repr'} disabled={disabled} />}
</div>
{this.state.show === 'hierarchy' && <ActionMenu items={this.hierarchyItems} onSelect={this.selectCurrent} />}
{this.state.show === 'add-repr' && <ActionMenu items={this.addActions} onSelect={this.selectAdd} />}
@@ -258,9 +251,9 @@ class VolumeRepresentationControls extends PurePluginUIComponent<{ representatio
{repr.obj?.label}
<small className='msp-25-lower-contrast-text' style={{ float: 'right' }}>{repr.obj?.description}</small>
</Button>
<IconButton svg={repr.state.isHidden ? VisibilityOffOutlined : VisibilityOutlined} toggleState={false} onClick={this.toggleVisible} title={`${repr.state.isHidden ? 'Show' : 'Hide'} component`} small className='msp-form-control' flex />
<IconButton svg={DeleteOutlined} onClick={this.remove} title='Remove' small />
<IconButton svg={MoreHoriz} onClick={this.toggleUpdate} title='Actions' toggleState={this.state.action === 'update'} />
<IconButton svg={repr.state.isHidden ? VisibilityOffOutlinedSvg : VisibilityOutlinedSvg} toggleState={false} onClick={this.toggleVisible} title={`${repr.state.isHidden ? 'Show' : 'Hide'} component`} small className='msp-form-control' flex />
<IconButton svg={DeleteOutlinedSvg} onClick={this.remove} title='Remove' small />
<IconButton svg={MoreHorizSvg} onClick={this.toggleUpdate} title='Actions' toggleState={this.state.action === 'update'} />
</div>
{this.state.action === 'update' && !!repr.parent && <div style={{ marginBottom: '6px' }} className='msp-accent-offset'>
<UpdateTransformControl state={repr.parent} transform={repr.transform} customHeader='none' noMargin />

View File

@@ -11,7 +11,7 @@ import { TaskManager } from '../mol-plugin/util/task-manager';
import { filter } from 'rxjs/operators';
import { Progress } from '../mol-task';
import { IconButton } from './controls/common';
import Cancel from '@material-ui/icons/Cancel';
import { CancelSvg } from './controls/icons';
export class BackgroundTaskProgress extends PluginUIComponent<{ }, { tracked: OrderedMap<number, TaskManager.ProgressEvent> }> {
componentDidMount() {
@@ -49,7 +49,7 @@ class ProgressEntry extends PluginUIComponent<{ event: TaskManager.ProgressEvent
return <div className='msp-task-state'>
<div>
{root.progress.canAbort && <IconButton svg={Cancel} onClick={this.abort} title='Abort' />}
{root.progress.canAbort && <IconButton svg={CancelSvg} onClick={this.abort} title='Abort' />}
<div>
{root.progress.message} {pr} {subtasks}
</div>

View File

@@ -10,7 +10,7 @@ import * as React from 'react';
import { PluginUIComponent } from './base';
import { PluginToastManager } from '../mol-plugin/util/toast';
import { IconButton } from './controls/common';
import Cancel from '@material-ui/icons/Cancel';
import { CancelSvg } from './controls/icons';
class ToastEntry extends PluginUIComponent<{ entry: PluginToastManager.Entry }> {
private hide = () => {
@@ -33,7 +33,7 @@ class ToastEntry extends PluginUIComponent<{ entry: PluginToastManager.Entry }>
</div>
<div className='msp-toast-clear'></div>
<div className='msp-toast-hide'>
<IconButton svg={Cancel} onClick={this.hide} title='Hide' className='msp-no-hover-outline' />
<IconButton svg={CancelSvg} onClick={this.hide} title='Hide' className='msp-no-hover-outline' />
</div>
</div>;
}

View File

@@ -5,12 +5,6 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Autorenew from '@material-ui/icons/Autorenew';
import BuildOutlined from '@material-ui/icons/BuildOutlined';
import CameraOutlined from '@material-ui/icons/CameraOutlined';
import Close from '@material-ui/icons/Close';
import Fullscreen from '@material-ui/icons/Fullscreen';
import Tune from '@material-ui/icons/Tune';
import * as React from 'react';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
@@ -23,6 +17,7 @@ import { ControlGroup, IconButton } from './controls/common';
import { ToggleSelectionModeButton } from './structure/selection';
import { DownloadScreenshotControls } from './viewport/screenshot';
import { SimpleSettingsControl } from './viewport/simple-settings';
import { AutorenewSvg, CameraOutlinedSvg, BuildOutlinedSvg, FullscreenSvg, TuneSvg, CloseSvg } from './controls/icons';
interface ViewportControlsState {
isSettingsExpanded: boolean,
@@ -93,17 +88,17 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
<div className='msp-viewport-controls-buttons'>
<div>
<div className='msp-semi-transparent-background' />
{this.icon(Autorenew, this.resetCamera, 'Reset Camera')}
{this.icon(AutorenewSvg, this.resetCamera, 'Reset Camera')}
</div>
<div>
<div className='msp-semi-transparent-background' />
{this.icon(CameraOutlined, this.toggleScreenshotExpanded, 'Screenshot / State Snapshot', this.state.isScreenshotExpanded)}
{this.icon(CameraOutlinedSvg, this.toggleScreenshotExpanded, 'Screenshot / State Snapshot', this.state.isScreenshotExpanded)}
</div>
<div>
<div className='msp-semi-transparent-background' />
{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)}
{this.icon(BuildOutlinedSvg, this.toggleControls, 'Toggle Controls Panel', this.plugin.layout.state.showControls)}
{this.plugin.config.get(PluginConfig.Viewport.ShowExpand) && this.icon(FullscreenSvg, this.toggleExpanded, 'Toggle Expanded Viewport', this.plugin.layout.state.isExpanded)}
{this.icon(TuneSvg, this.toggleSettingsExpanded, 'Settings / Controls Info', this.state.isSettingsExpanded)}
</div>
{this.plugin.config.get(PluginConfig.Viewport.ShowSelectionMode) && <div>
<div className='msp-semi-transparent-background' />
@@ -112,13 +107,13 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
</div>
{this.state.isScreenshotExpanded && <div className='msp-viewport-controls-panel'>
<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'>
topRightIcon={CloseSvg} 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' title='Click to close.' initialExpanded={true} hideExpander={true} hideOffset={true} onHeaderClick={this.toggleSettingsExpanded}
topRightIcon={Close} noTopMargin childrenClassName='msp-viewport-controls-panel-controls'>
topRightIcon={CloseSvg} noTopMargin childrenClassName='msp-viewport-controls-panel-controls'>
<SimpleSettingsControl />
</ControlGroup>
</div>}

View File

@@ -10,11 +10,8 @@ import { PluginUIComponent } from '../base';
import { StateTransformer, StateSelection } from '../../mol-state';
import { SelectLoci } from '../../mol-plugin/behavior/dynamic/representation';
import { FocusLoci } from '../../mol-plugin/behavior/dynamic/representation';
import { Icon } from '../controls/icons';
import { Icon, ArrowDropDownSvg, ArrowRightSvg, CameraSvg } from '../controls/icons';
import { Button } from '../controls/common';
import ArrowRight from '@material-ui/icons/ArrowRight';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
import Camera from '@material-ui/icons/Camera';
function getBindingsList(bindings: { [k: string]: Binding }) {
return Object.keys(bindings).map(k => [k, bindings[k]] as [string, Binding]);
@@ -60,7 +57,7 @@ export class HelpGroup extends React.PureComponent<{ header: string, initiallyEx
return <div className='msp-control-group-wrapper'>
<div className='msp-control-group-header'>
<Button onClick={this.toggleExpanded}>
<Icon svg={this.state.isExpanded ? ArrowDropDown : ArrowRight} />
<Icon svg={this.state.isExpanded ? ArrowDropDownSvg : ArrowRightSvg} />
{this.props.header}
</Button>
</div>
@@ -155,7 +152,7 @@ export class HelpContent extends PluginUIComponent {
<HelpSection header='How-to Guides' />
<HelpGroup header='Create an Image'>
<HelpText>
<p>Use the <Icon svg={Camera} /> icon in the viewport to bring up the screenshot controls.</p>
<p>Use the <Icon svg={CameraSvg} /> icon in the viewport to bring up the screenshot controls.</p>
<p>To adjust the size of the image, use the <i>Resolution</i> dropdown.</p>
</HelpText>
</HelpGroup>

View File

@@ -14,10 +14,9 @@ import { Subject } from 'rxjs';
import { ViewportScreenshotHelper } from '../../mol-plugin/util/viewport-screenshot';
import { Button, ExpandGroup } from '../controls/common';
import { CameraHelperProps } from '../../mol-canvas3d/helper/camera-helper';
import GetApp from '@material-ui/icons/GetApp';
import Launch from '@material-ui/icons/Launch';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateExportImportControls, LocalStateSnapshotParams } from '../state/snapshots';
import { GetAppSvg, LaunchSvg } from '../controls/icons';
interface ImageControlsState {
showPreview: boolean
@@ -141,8 +140,8 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
<span>Right-click the image to Copy.</span>
</div>
<div className='msp-flex-row'>
<Button icon={GetApp} onClick={this.download} disabled={this.state.isDisabled}>Download</Button>
<Button icon={Launch} onClick={this.openTab} disabled={this.state.isDisabled}>Open in new Tab</Button>
<Button icon={GetAppSvg} onClick={this.download} disabled={this.state.isDisabled}>Download</Button>
<Button icon={LaunchSvg} onClick={this.openTab} disabled={this.state.isDisabled}>Open in new Tab</Button>
</div>
<ParameterControls params={this.plugin.helpers.viewportScreenshot!.params} values={this.plugin.helpers.viewportScreenshot!.values} onChange={this.setProps} isDisabled={this.state.isDisabled} />
<ExpandGroup header='State'>

View File

@@ -62,7 +62,10 @@ const SimpleSettingsParams = {
outline: Canvas3DParams.postprocessing.params.outline,
fog: Canvas3DParams.cameraFog,
}, { pivot: 'renderStyle' }),
clipping: Canvas3DParams.cameraClipping,
clipping: PD.Group<any>({
...Canvas3DParams.cameraClipping.params,
...(Canvas3DParams.renderer.params.clip as any).params as any
}, { pivot: 'radius' }),
layout: PD.MultiSelect([] as LayoutOptions[], PD.objectToOptions(LayoutOptions)),
};
@@ -108,7 +111,10 @@ const SimpleSettingsMapping = ParamMapping({
outline: canvas.postprocessing.outline,
fog: canvas.cameraFog
},
clipping: canvas.cameraClipping
clipping: {
...canvas.cameraClipping,
...canvas.renderer.clip
}
};
},
update(s, props) {
@@ -122,7 +128,14 @@ const SimpleSettingsMapping = ParamMapping({
canvas.postprocessing.occlusion = s.lighting.occlusion;
canvas.postprocessing.outline = s.lighting.outline;
canvas.cameraFog = s.lighting.fog;
canvas.cameraClipping = s.clipping;
canvas.cameraClipping = {
radius: s.clipping.radius,
far: s.clipping.far,
};
canvas.renderer.clip = {
variant: s.clipping.variant,
objects: s.clipping.objects,
};
props.layout = s.layout;
},

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 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 Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -22,6 +22,7 @@ import { getQualityProps } from './util';
import { BaseGeometry } from '../mol-geo/geometry/base';
import { Visual } from './visual';
import { CustomProperty } from '../mol-model-props/common/custom-property';
import { Clipping } from '../mol-theme/clipping';
// export interface RepresentationProps {
// visuals?: string[]
@@ -173,6 +174,8 @@ namespace Representation {
overpaint: Overpaint
/** Per group transparency applied to the representation's renderobjects */
transparency: Transparency
/** Bit mask of per group clipping applied to the representation's renderobjects */
clipping: Clipping
/** Controls if the representation's renderobjects are synced automatically with GPU or not */
syncManually: boolean
/** A transformation applied to the representation's renderobjects */
@@ -181,7 +184,7 @@ namespace Representation {
markerActions: MarkerActions
}
export function createState(): State {
return { visible: true, alphaFactor: 1, pickable: true, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty, markerActions: MarkerActions.All };
return { visible: true, alphaFactor: 1, pickable: true, syncManually: false, transform: Mat4.identity(), overpaint: Overpaint.Empty, transparency: Transparency.Empty, clipping: Clipping.Empty, markerActions: MarkerActions.All };
}
export function updateState(state: State, update: Partial<State>) {
if (update.visible !== undefined) state.visible = update.visible;
@@ -189,6 +192,7 @@ namespace Representation {
if (update.pickable !== undefined) state.pickable = update.pickable;
if (update.overpaint !== undefined) state.overpaint = update.overpaint;
if (update.transparency !== undefined) state.transparency = update.transparency;
if (update.clipping !== undefined) state.clipping = update.clipping;
if (update.syncManually !== undefined) state.syncManually = update.syncManually;
if (update.transform !== undefined) Mat4.copy(state.transform, update.transform);
if (update.markerActions !== undefined) state.markerActions = update.markerActions;

View File

@@ -18,6 +18,7 @@ import { EmptyLoci, Loci, isEveryLoci, isDataLoci } from '../../mol-model/loci';
import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
import { Overpaint } from '../../mol-theme/overpaint';
import { StructureParams } from './params';
import { Clipping } from '../../mol-theme/clipping';
export function ComplexRepresentation<P extends StructureParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: (materialId: number) => ComplexVisual<P>): StructureRepresentation<P> {
let version = 0;
@@ -94,6 +95,11 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
visual.setOverpaint(remappedOverpaint);
}
if (state.transparency !== undefined && visual) visual.setTransparency(state.transparency);
if (state.clipping !== undefined && visual) {
// Remap loci from equivalent structure to the current structure
const remappedClipping = Clipping.remap(state.clipping, _structure);
visual.setClipping(remappedClipping);
}
if (state.transform !== undefined && visual) visual.setTransform(state.transform);
if (state.unitTransforms !== undefined && visual) {
// Since ComplexVisuals always renders geometries between units, the application

View File

@@ -30,6 +30,7 @@ import { SizeTheme } from '../../mol-theme/size';
import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
import { createMarkers } from '../../mol-geo/geometry/marker-data';
import { StructureParams, StructureMeshParams, StructureTextParams, StructureDirectVolumeParams } from './params';
import { Clipping } from '../../mol-theme/clipping';
export interface ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
@@ -217,6 +218,9 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
setTransparency(transparency: Transparency) {
Visual.setTransparency(renderObject, transparency, lociApply, true);
},
setClipping(clipping: Clipping) {
Visual.setClipping(renderObject, clipping, lociApply, true);
},
destroy() {
// TODO
renderObject = undefined;

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Structure } from '../../../mol-model/structure';
import { Structure, Model } from '../../../mol-model/structure';
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
@@ -46,6 +46,6 @@ export const CarbohydrateRepresentationProvider = StructureRepresentationProvide
defaultColorTheme: { name: 'carbohydrate-symbol' },
defaultSizeTheme: { name: 'uniform' },
isApplicable: (structure: Structure) => {
return structure.models.reduce((a, v) => a + v.properties.saccharideComponentMap.size, 0) > 0;
return structure.models.some(m => Model.hasCarbohydrate(m));
}
});

View File

@@ -23,6 +23,7 @@ import { Transparency } from '../../mol-theme/transparency';
import { Mat4, EPSILON } from '../../mol-math/linear-algebra';
import { Interval } from '../../mol-data/int';
import { StructureParams } from './params';
import { Clipping } from '../../mol-theme/clipping';
export interface UnitsVisual<P extends StructureParams> extends Visual<StructureGroup, P> { }
@@ -191,13 +192,14 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
}
function setVisualState(visual: UnitsVisual<P>, group: Unit.SymmetryGroup, state: Partial<StructureRepresentationState>) {
const { visible, alphaFactor, pickable, overpaint, transparency, transform, unitTransforms } = state;
const { visible, alphaFactor, pickable, overpaint, transparency, clipping, transform, unitTransforms } = state;
if (visible !== undefined) visual.setVisibility(visible);
if (alphaFactor !== undefined) visual.setAlphaFactor(alphaFactor);
if (pickable !== undefined) visual.setPickable(pickable);
if (overpaint !== undefined) visual.setOverpaint(overpaint);
if (transparency !== undefined) visual.setTransparency(transparency);
if (clipping !== undefined) visual.setClipping(clipping);
if (transform !== undefined) visual.setTransform(transform);
if (unitTransforms !== undefined) {
if (unitTransforms) {
@@ -210,7 +212,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
}
function setState(state: Partial<StructureRepresentationState>) {
const { visible, alphaFactor, pickable, overpaint, transparency, transform, unitTransforms, syncManually, markerActions } = state;
const { visible, alphaFactor, pickable, overpaint, transparency, clipping, transform, unitTransforms, syncManually, markerActions } = state;
const newState: Partial<StructureRepresentationState> = {};
if (visible !== _state.visible) newState.visible = visible;
@@ -224,6 +226,11 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
if (transparency !== undefined && !Transparency.areEqual(transparency, _state.transparency)) {
newState.transparency = transparency;
}
if (clipping !== undefined && !Clipping.areEqual(clipping, _state.clipping)) {
if (_structure) {
newState.clipping = Clipping.remap(clipping, _structure);
}
}
if (transform !== undefined && !Mat4.areEqual(transform, _state.transform, EPSILON)) {
newState.transform = transform;
}

View File

@@ -36,6 +36,7 @@ import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume
import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
import { SizeValues } from '../../mol-gl/renderable/schema';
import { StructureParams, StructureMeshParams, StructureSpheresParams, StructurePointsParams, StructureLinesParams, StructureTextParams, StructureDirectVolumeParams, StructureTextureMeshParams } from './params';
import { Clipping } from '../../mol-theme/clipping';
export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup }
@@ -268,6 +269,9 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
setTransparency(transparency: Transparency) {
Visual.setTransparency(renderObject, transparency, lociApply, true);
},
setClipping(clipping: Clipping) {
Visual.setClipping(renderObject, clipping, lociApply, true);
},
destroy() {
// TODO
renderObject = undefined;

View File

@@ -10,10 +10,11 @@ import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { LocationIterator } from '../../../../mol-geo/util/location-iterator';
import { StructureGroup } from '../../units-visual';
import { LinkCylinderParams } from './link';
import { ObjectKeys } from '../../../../mol-util/type-helpers';
export const BondCylinderParams = {
...LinkCylinderParams,
includeTypes: PD.MultiSelect(Object.keys(BondType.Names) as BondType.Names[], PD.objectToOptions(BondType.Names)),
includeTypes: PD.MultiSelect(ObjectKeys(BondType.Names), PD.objectToOptions(BondType.Names)),
excludeTypes: PD.MultiSelect([] as BondType.Names[], PD.objectToOptions(BondType.Names)),
};
export const DefaultBondCylinderProps = PD.getDefaultValues(BondCylinderParams);

View File

@@ -21,6 +21,8 @@ import { createOverpaint, clearOverpaint, applyOverpaintColor } from '../mol-geo
import { Interval } from '../mol-data/int';
import { Transparency } from '../mol-theme/transparency';
import { createTransparency, clearTransparency, applyTransparencyValue } from '../mol-geo/geometry/transparency-data';
import { Clipping } from '../mol-theme/clipping';
import { createClipping, applyClippingGroups, clearClipping } from '../mol-geo/geometry/clipping-data';
export interface VisualContext {
readonly runtime: RuntimeContext
@@ -42,6 +44,7 @@ interface Visual<D, P extends PD.Params> {
setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array | null) => void
setOverpaint: (overpaint: Overpaint) => void
setTransparency: (transparency: Transparency) => void
setClipping: (clipping: Clipping) => void
destroy: () => void
}
namespace Visual {
@@ -128,6 +131,31 @@ namespace Visual {
ValueCell.update(tTransparency, tTransparency.ref.value);
}
export function setClipping(renderObject: GraphicsRenderObject | undefined, clipping: Clipping, lociApply: LociApply, clear: boolean) {
if (!renderObject) return;
const { tClipping, uGroupCount, instanceCount } = renderObject.values;
const count = uGroupCount.ref.value * instanceCount.ref.value;
// ensure texture has right size
createClipping(clipping.layers.length ? count : 0, renderObject.values);
const { array } = tClipping.ref.value;
// clear if requested
if (clear) clearClipping(array, 0, count);
for (let i = 0, il = clipping.layers.length; i < il; ++i) {
const { loci, groups } = clipping.layers[i];
const apply = (interval: Interval) => {
const start = Interval.start(interval);
const end = Interval.end(interval);
return applyClippingGroups(array, start, end, groups);
};
lociApply(loci, apply, false);
}
ValueCell.update(tClipping, tClipping.ref.value);
}
export function setTransform(renderObject: GraphicsRenderObject | undefined, transform?: Mat4, instanceTransforms?: Float32Array | null) {
if (!renderObject || (!transform && !instanceTransforms)) return;

View File

@@ -29,6 +29,7 @@ import { BaseGeometry } from '../../mol-geo/geometry/base';
import { Subject } from 'rxjs';
import { Task } from '../../mol-task';
import { SizeValues } from '../../mol-gl/renderable/schema';
import { Clipping } from '../../mol-theme/clipping';
export interface VolumeVisual<P extends VolumeParams> extends Visual<Volume, P> { }
@@ -191,6 +192,9 @@ export function VolumeVisual<G extends Geometry, P extends VolumeParams & Geomet
setTransparency(transparency: Transparency) {
return Visual.setTransparency(renderObject, transparency, lociApply, true);
},
setClipping(clipping: Clipping) {
return Visual.setClipping(renderObject, clipping, lociApply, true);
},
destroy() {
// TODO
renderObject = undefined;

197
src/mol-theme/clipping.ts Normal file
View File

@@ -0,0 +1,197 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Loci } from '../mol-model/loci';
import { StructureElement, Structure } from '../mol-model/structure';
import { Script } from '../mol-script/script';
import BitFlags from '../mol-util/bit-flags';
export { Clipping };
type Clipping = { readonly layers: ReadonlyArray<Clipping.Layer> }
function Clipping(layers: ReadonlyArray<Clipping.Layer>): Clipping {
return { layers };
}
namespace Clipping {
export type Layer = { readonly loci: StructureElement.Loci, readonly groups: Groups }
export const Empty: Clipping = { layers: [] };
export type Groups = BitFlags<Groups.Flag>
export namespace Groups {
export const is: (g: Groups, f: Flag) => boolean = BitFlags.has;
export const enum Flag {
None = 0x0,
One = 0x1,
Two = 0x2,
Three = 0x4,
Four = 0x8,
Five = 0x10,
Six = 0x20,
}
export function create(flags: Flag): Groups {
return BitFlags.create(flags);
}
export const Names = {
'one': Flag.One,
'two': Flag.Two,
'three': Flag.Three,
'four': Flag.Four,
'five': Flag.Five,
'six': Flag.Six,
};
export type Names = keyof typeof Names
export function isName(name: string): name is Names {
return name in Names;
}
export function fromName(name: Names): Flag {
switch (name) {
case 'one': return Flag.One;
case 'two': return Flag.Two;
case 'three': return Flag.Three;
case 'four': return Flag.Four;
case 'five': return Flag.Five;
case 'six': return Flag.Six;
}
}
export function fromNames(names: Names[]): Flag {
let f = Flag.None;
for (let i = 0, il = names.length; i < il; ++i) {
f |= fromName(names[i]);
}
return f;
}
export function toNames(groups: Groups): Names[] {
let names: Names[] = [];
if (is(groups, Flag.One)) names.push('one');
if (is(groups, Flag.Two)) names.push('two');
if (is(groups, Flag.Three)) names.push('three');
if (is(groups, Flag.Four)) names.push('four');
if (is(groups, Flag.Five)) names.push('five');
if (is(groups, Flag.Six)) names.push('six');
return names;
}
}
/** Clip object types */
export const Type = {
none: 0, // to switch clipping off
plane: 1,
sphere: 2,
cube: 3,
cylinder: 4,
infiniteCone: 5,
};
export type Variant = 'instance' | 'pixel'
export function areEqual(cA: Clipping, cB: Clipping) {
if (cA.layers.length === 0 && cB.layers.length === 0) return true;
if (cA.layers.length !== cB.layers.length) return false;
for (let i = 0, il = cA.layers.length; i < il; ++i) {
if (cA.layers[i].groups !== cB.layers[i].groups) return false;
if (!Loci.areEqual(cA.layers[i].loci, cB.layers[i].loci)) return false;
}
return true;
}
export function isEmpty(clipping: Clipping) {
return clipping.layers.length === 0;
}
export function remap(clipping: Clipping, structure: Structure) {
const layers: Clipping.Layer[] = [];
for (const layer of clipping.layers) {
let { loci, groups } = layer;
loci = StructureElement.Loci.remap(loci, structure);
if (!StructureElement.Loci.isEmpty(loci)) {
layers.push({ loci, groups });
}
}
return { layers };
}
export function merge(clipping: Clipping): Clipping {
if (isEmpty(clipping)) return clipping;
const { structure } = clipping.layers[0].loci;
const map = new Map<Groups, StructureElement.Loci>();
let shadowed = StructureElement.Loci.none(structure);
for (let i = 0, il = clipping.layers.length; i < il; ++i) {
let { loci, groups } = clipping.layers[il - i - 1]; // process from end
loci = StructureElement.Loci.subtract(loci, shadowed);
shadowed = StructureElement.Loci.union(loci, shadowed);
if (!StructureElement.Loci.isEmpty(loci)) {
if (map.has(groups)) {
loci = StructureElement.Loci.union(loci, map.get(groups)!);
}
map.set(groups, loci);
}
}
const layers: Clipping.Layer[] = [];
map.forEach((loci, groups) => {
layers.push({ loci, groups });
});
return { layers };
}
export function filter(clipping: Clipping, filter: Structure): Clipping {
if (isEmpty(clipping)) return clipping;
const { structure } = clipping.layers[0].loci;
const layers: Clipping.Layer[] = [];
for (const layer of clipping.layers) {
let { loci, groups } = layer;
// filter by first map to the `filter` structure and
// then map back to the original structure of the clipping loci
const filtered = StructureElement.Loci.remap(loci, filter);
loci = StructureElement.Loci.remap(filtered, structure);
if (!StructureElement.Loci.isEmpty(loci)) {
layers.push({ loci, groups });
}
}
return { layers };
}
export type ScriptLayer = { script: Script, groups: Groups }
export function ofScript(scriptLayers: ScriptLayer[], structure: Structure): Clipping {
const layers: Clipping.Layer[] = [];
for (let i = 0, il = scriptLayers.length; i < il; ++i) {
const { script, groups } = scriptLayers[i];
const loci = Script.toLoci(script, structure);
if (!StructureElement.Loci.isEmpty(loci)) {
layers.push({ loci, groups });
}
}
return { layers };
}
export type BundleLayer = { bundle: StructureElement.Bundle, groups: Groups }
export function ofBundle(bundleLayers: BundleLayer[], structure: Structure): Clipping {
const layers: Clipping.Layer[] = [];
for (let i = 0, il = bundleLayers.length; i < il; ++i) {
const { bundle, groups } = bundleLayers[i];
const loci = StructureElement.Bundle.toLoci(bundle, structure.root);
layers.push({ loci, groups });
}
return { layers };
}
export function toBundle(clipping: Clipping) {
const layers: BundleLayer[] = [];
for (let i = 0, il = clipping.layers.length; i < il; ++i) {
let { loci, groups } = clipping.layers[i];
const bundle = StructureElement.Bundle.fromLoci(loci);
layers.push({ bundle, groups });
}
return { layers };
}
}

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StructureElement, Bond, ElementIndex, Unit } from '../../mol-model/structure';
import { StructureElement, Bond, ElementIndex, Unit, Model } from '../../mol-model/structure';
import { SaccharideColors, MonosaccharidesColorTable } from '../../mol-model/structure/structure/carbohydrates/constants';
import { Location } from '../../mol-model/location';
import { ColorTheme, LocationColor } from '../color';
@@ -68,6 +68,6 @@ export const CarbohydrateSymbolColorThemeProvider: ColorTheme.Provider<Carbohydr
getParams: getCarbohydrateSymbolColorThemeParams,
defaultValues: PD.getDefaultValues(CarbohydrateSymbolColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => {
return !!ctx.structure && ctx.structure.models.reduce((a, v) => a + v.properties.saccharideComponentMap.size, 0) > 0;
return !!ctx.structure && ctx.structure.models.some(m => Model.hasCarbohydrate(m));
}
};

View File

@@ -27,11 +27,11 @@ namespace Transparency {
return true;
}
export function ofScript(script: Script, value: number, variant: Transparency.Variant, structure: Structure): Transparency {
export function ofScript(script: Script, value: number, variant: Variant, structure: Structure): Transparency {
return { loci: Script.toLoci(script, structure), value, variant };
}
export function ofBundle(bundle: StructureElement.Bundle, value: number, variant: Transparency.Variant, structure: Structure): Transparency {
export function ofBundle(bundle: StructureElement.Bundle, value: number, variant: Variant, structure: Structure): Transparency {
return { loci: StructureElement.Bundle.toLoci(bundle, structure), value, variant };
}
}

View File

@@ -423,7 +423,7 @@ export namespace ParamDefinition {
return false;
}
export function merge(params: Params, a: any, b: any): any {
export function merge<P extends Params>(params: P, a: any, b: any): Values<P> {
if (a === undefined) return { ...b };
if (b === undefined) return { ...a };

View File

@@ -6,7 +6,7 @@
import './index.html';
import { resizeCanvas } from '../../mol-canvas3d/util';
import { Canvas3DParams } from '../../mol-canvas3d/canvas3d';
import { Canvas3DParams, Canvas3D } from '../../mol-canvas3d/canvas3d';
import { ColorNames } from '../../mol-util/color/names';
import { PositionData, Box3D, Sphere3D } from '../../mol-math/geometry';
import { OrderedSet } from '../../mol-data/int';
@@ -31,10 +31,10 @@ const canvas = document.createElement('canvas');
parent.appendChild(canvas);
resizeCanvas(canvas, parent);
const canvas3d = PD.merge(Canvas3DParams, PD.getDefaultValues(Canvas3DParams), {
const canvas3d = Canvas3D.fromCanvas(canvas, PD.merge(Canvas3DParams, PD.getDefaultValues(Canvas3DParams), {
renderer: { backgroundColor: ColorNames.white },
camera: { mode: 'orthographic' }
});
}));
canvas3d.animate();
async function init() {

View File

@@ -3,7 +3,6 @@ const webpack = require('webpack');
const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const VersionFile = require('webpack-version-file-plugin');
// const CircularDependencyPlugin = require('circular-dependency-plugin');
const sharedConfig = {
module: {
@@ -20,18 +19,12 @@ const sharedConfig = {
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { sourceMap: false } },
{ loader: 'resolve-url-loader', options: { sourceMap: false } },
{ loader: 'sass-loader', options: { sourceMap: false } },
]
}
]
},
plugins: [
// new CircularDependencyPlugin({
// include: [ path.resolve(__dirname, 'lib/') ],
// failOnError: false,
// cwd: process.cwd(),
// }),
new ExtraWatchWebpackPlugin({
files: [
'./lib/**/*.scss',
@@ -39,8 +32,6 @@ const sharedConfig = {
],
}),
new webpack.DefinePlugin({
// __VERSION__: webpack.DefinePlugin.runtimeValue(() => JSON.stringify(require('./package.json').version), true),
// __VERSION_TIMESTAMP__: webpack.DefinePlugin.runtimeValue(() => `${new Date().valueOf()}`, true),
'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
}),
new MiniCssExtractPlugin({ filename: 'molstar.css', }),