mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a76a3fa64 | ||
|
|
71bf4e21f5 | ||
|
|
e0d36c30d3 | ||
|
|
b53debcfef | ||
|
|
d0705ac226 | ||
|
|
e01eacb3fe | ||
|
|
d4102b476b | ||
|
|
83ce17174a | ||
|
|
18023d7f26 | ||
|
|
a8541d5967 | ||
|
|
8b21818f2e | ||
|
|
0b290247dc | ||
|
|
fb5010e962 | ||
|
|
178789d327 | ||
|
|
4fae526073 | ||
|
|
05f1d8085a | ||
|
|
38bbabd742 | ||
|
|
3ab958a93c | ||
|
|
f59d589a30 | ||
|
|
11f7e54704 | ||
|
|
16ebd8266e | ||
|
|
7a796a4d3d | ||
|
|
1cbb915962 | ||
|
|
80486d58c3 | ||
|
|
81bc116c4d | ||
|
|
4249064dd1 |
39
CHANGELOG.md
Normal file
39
CHANGELOG.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Change Log
|
||||
All notable changes to this project will be documented in this file, following the suggestions of [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to [Semantic Versioning](http://semver.org/) for its most widely used - and defacto - public interfaces.
|
||||
|
||||
Note that since we don't clearly distinguish between a public and private interfaces there will be changes in non-major versions that are potentially breaking. If we make breaking changes to less used interfaces we will highlight it in here.
|
||||
|
||||
## [v2.0.4] - 2021-04-20
|
||||
|
||||
- [WIP] Mesh export extension
|
||||
- ``Structure.eachAtomicHierarchyElement`` (#161)
|
||||
- Fixed reading multi-line values in SDF format
|
||||
- Fixed Measurements UI labels (#166)
|
||||
|
||||
## [v2.0.3] - 2021-04-09
|
||||
### Added
|
||||
- Support for ``ColorTheme.palette`` designed for providing gradient-like coloring.
|
||||
|
||||
### Changed
|
||||
- [Breaking] The `zip` function is now asynchronous and expects a `RuntimeContext`. Also added `Zip()` returning a `Task`.
|
||||
- [Breaking] Add ``CubeGridFormat`` in ``alpha-orbitals`` extension.
|
||||
|
||||
## [v2.0.2] - 2021-03-29
|
||||
### Added
|
||||
- `Canvas3D.getRenderObjects`.
|
||||
- [WIP] Animate state interpolating, including model trajectories
|
||||
|
||||
### Changed
|
||||
- Recognise MSE, SEP, TPO, PTR and PCA as non-standard amino-acids.
|
||||
|
||||
### Fixed
|
||||
- VolumeFromDensityServerCif transform label
|
||||
|
||||
|
||||
## [v2.0.1] - 2021-03-23
|
||||
### Fixed
|
||||
- Exclude tsconfig.commonjs.tsbuildinfo from npm bundle
|
||||
|
||||
|
||||
## [v2.0.0] - 2021-03-23
|
||||
Too many changes to list as this is the start of the changelog... Notably, default exports are now forbidden.
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/argparse": "^1.0.38",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.4",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { CellPack } from '../../extensions/cellpack';
|
||||
import { DnatcoConfalPyramids } from '../../extensions/dnatco';
|
||||
import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
|
||||
import { Mp4Export } from '../../extensions/mp4-export';
|
||||
import { GeometryExport } from '../../extensions/geo-export';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
|
||||
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
|
||||
@@ -55,7 +56,8 @@ const Extensions = {
|
||||
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
|
||||
'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
|
||||
'g3d': PluginSpec.Behavior(G3DFormat),
|
||||
'mp4-export': PluginSpec.Behavior(Mp4Export)
|
||||
'mp4-export': PluginSpec.Behavior(Mp4Export),
|
||||
'geo-export': PluginSpec.Behavior(GeometryExport)
|
||||
};
|
||||
|
||||
const DefaultViewerOptions = {
|
||||
|
||||
51
src/examples/basic-wrapper/custom-theme.ts
Normal file
51
src/examples/basic-wrapper/custom-theme.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { isPositionLocation } from '../../mol-geo/util/location-iterator';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { ColorTheme } from '../../mol-theme/color';
|
||||
import { ThemeDataContext } from '../../mol-theme/theme';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
|
||||
export function CustomColorTheme(
|
||||
ctx: ThemeDataContext,
|
||||
props: PD.Values<{}>
|
||||
): ColorTheme<{}> {
|
||||
const { radius, center } = ctx.structure?.boundary.sphere!;
|
||||
const radiusSq = Math.max(radius * radius, 0.001);
|
||||
const scale = ColorTheme.PaletteScale;
|
||||
|
||||
return {
|
||||
factory: CustomColorTheme,
|
||||
granularity: 'vertex',
|
||||
color: location => {
|
||||
if (!isPositionLocation(location)) return ColorNames.black;
|
||||
const dist = Vec3.squaredDistance(location.position, center);
|
||||
const t = Math.min(dist / radiusSq, 1);
|
||||
return ((t * scale) | 0) as Color;
|
||||
},
|
||||
palette: {
|
||||
filter: 'nearest',
|
||||
colors: [
|
||||
ColorNames.red,
|
||||
ColorNames.pink,
|
||||
ColorNames.violet,
|
||||
ColorNames.orange,
|
||||
ColorNames.yellow,
|
||||
ColorNames.green,
|
||||
ColorNames.blue
|
||||
]
|
||||
},
|
||||
props: props,
|
||||
description: '',
|
||||
};
|
||||
}
|
||||
|
||||
export const CustomColorThemeProvider: ColorTheme.Provider<{}, 'basic-wrapper-custom-color-theme'> = {
|
||||
name: 'basic-wrapper-custom-color-theme',
|
||||
label: 'Custom Color Theme',
|
||||
category: ColorTheme.Category.Misc,
|
||||
factory: CustomColorTheme,
|
||||
getParams: () => ({}),
|
||||
defaultValues: { },
|
||||
isApplicable: (ctx: ThemeDataContext) => true,
|
||||
};
|
||||
@@ -97,6 +97,7 @@
|
||||
addHeader('Misc');
|
||||
|
||||
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
|
||||
addControl('Apply Custom Theme', () => BasicMolStarWrapper.coloring.applyCustomTheme());
|
||||
addControl('Default Coloring', () => BasicMolStarWrapper.coloring.applyDefault());
|
||||
|
||||
addHeader('Interactivity');
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Asset } from '../../mol-util/assets';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { StripedResidues } from './coloring';
|
||||
import { CustomToastMessage } from './controls';
|
||||
import { CustomColorThemeProvider } from './custom-theme';
|
||||
import './index.html';
|
||||
import { buildStaticSuperposition, dynamicSuperpositionTest, StaticSuperpositionTestData } from './superposition';
|
||||
require('mol-plugin-ui/skin/light.scss');
|
||||
@@ -42,6 +43,7 @@ class BasicWrapper {
|
||||
});
|
||||
|
||||
this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
|
||||
this.plugin.representation.structure.themes.colorThemeRegistry.add(CustomColorThemeProvider);
|
||||
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
|
||||
}
|
||||
@@ -103,6 +105,13 @@ class BasicWrapper {
|
||||
}
|
||||
});
|
||||
},
|
||||
applyCustomTheme: async () => {
|
||||
this.plugin.dataTransaction(async () => {
|
||||
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
|
||||
await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: CustomColorThemeProvider.name as any });
|
||||
}
|
||||
});
|
||||
},
|
||||
applyDefault: async () => {
|
||||
this.plugin.dataTransaction(async () => {
|
||||
for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Grid } from '../../mol-model/volume';
|
||||
import { SphericalBasisOrder } from './spherical-functions';
|
||||
import { Box3D, RegularGrid3d } from '../../mol-math/geometry';
|
||||
import { arrayMin, arrayMax, arrayRms, arrayMean } from '../../mol-util/array';
|
||||
import { ModelFormat } from '../../mol-model-formats/format';
|
||||
|
||||
// Note: generally contracted gaussians are currently not supported.
|
||||
export interface SphericalElectronShell {
|
||||
@@ -59,6 +60,17 @@ export interface CubeGrid {
|
||||
isovalues?: { negative?: number; positive?: number };
|
||||
}
|
||||
|
||||
export type CubeGridFormat = ModelFormat<CubeGrid>;
|
||||
|
||||
// eslint-disable-next-line
|
||||
export function CubeGridFormat(grid: CubeGrid): CubeGridFormat {
|
||||
return { name: 'custom grid', kind: 'cube-grid', data: grid };
|
||||
}
|
||||
|
||||
export function isCubeGridData(f: ModelFormat): f is CubeGridFormat {
|
||||
return f.kind === 'cube-grid';
|
||||
}
|
||||
|
||||
export function initCubeGrid(params: CubeGridComputationParams): CubeGridInfo {
|
||||
const geometry = params.basis.atoms.map(a => a.center);
|
||||
const { gridSpacing: spacing, boxExpand: expand } = params;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers
|
||||
import { StateTransformer } from '../../mol-state';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { VolumeRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
|
||||
import { AlphaOrbital, Basis, CubeGrid } from './data-model';
|
||||
import { AlphaOrbital, Basis, CubeGrid, CubeGridFormat, isCubeGridData } from './data-model';
|
||||
import { createSphericalCollocationDensityGrid } from './density';
|
||||
import { Tensor } from '../../mol-math/linear-algebra';
|
||||
|
||||
@@ -114,7 +114,7 @@ export const CreateOrbitalVolume = PluginStateTransform.BuiltIn({
|
||||
}, a.data.orbitals[params.index], plugin.canvas3d?.webgl).runInContext(ctx);
|
||||
const volume: Volume = {
|
||||
grid: data.grid,
|
||||
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
|
||||
sourceData: CubeGridFormat(data),
|
||||
customProperties: new CustomProperties(),
|
||||
_propertyData: Object.create(null),
|
||||
};
|
||||
@@ -146,7 +146,7 @@ export const CreateOrbitalDensityVolume = PluginStateTransform.BuiltIn({
|
||||
}, a.data.orbitals, plugin.canvas3d?.webgl).runInContext(ctx);
|
||||
const volume: Volume = {
|
||||
grid: data.grid,
|
||||
sourceData: { name: 'custom grid', kind: 'alpha-orbitals', data },
|
||||
sourceData: CubeGridFormat(data),
|
||||
customProperties: new CustomProperties(),
|
||||
_propertyData: Object.create(null),
|
||||
};
|
||||
@@ -210,9 +210,9 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
});
|
||||
|
||||
function volumeParams(plugin: PluginContext, volume: PluginStateObject.Volume.Data, params: StateTransformer.Params<typeof CreateOrbitalRepresentation3D>) {
|
||||
if (volume.data.sourceData.kind !== 'alpha-orbitals') throw new Error('Invalid data source kind.');
|
||||
if (!isCubeGridData(volume.data.sourceData)) throw new Error('Invalid data source kind.');
|
||||
|
||||
const { isovalues } = volume.data.sourceData.data as CubeGrid;
|
||||
const { isovalues } = volume.data.sourceData.data;
|
||||
if (!isovalues) throw new Error('Isovalues are not computed.');
|
||||
|
||||
const value = isovalues[params.kind];
|
||||
|
||||
69
src/extensions/geo-export/controls.ts
Normal file
69
src/extensions/geo-export/controls.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
*/
|
||||
|
||||
import { PluginComponent } from '../../mol-plugin-state/component';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { Task } from '../../mol-task';
|
||||
import { ObjExporter } from './export';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateSelection } from '../../mol-state';
|
||||
import { SetUtils } from '../../mol-util/set';
|
||||
import { zip } from '../../mol-util/zip/zip';
|
||||
|
||||
export class GeometryControls extends PluginComponent {
|
||||
getFilename() {
|
||||
const models = this.plugin.state.data.select(StateSelection.Generators.rootsOfType(PluginStateObject.Molecule.Model)).map(s => s.obj!.data);
|
||||
const uniqueIds = new Set<string>();
|
||||
models.forEach(m => uniqueIds.add(m.entryId.toUpperCase()));
|
||||
const idString = SetUtils.toArray(uniqueIds).join('-');
|
||||
return `${idString || 'molstar-model'}`;
|
||||
}
|
||||
|
||||
exportObj() {
|
||||
const task = Task.create('Export OBJ', async ctx => {
|
||||
try {
|
||||
const renderObjects = this.plugin.canvas3d?.getRenderObjects()!;
|
||||
|
||||
const filename = this.getFilename();
|
||||
const objExporter = new ObjExporter(filename);
|
||||
for (let i = 0, il = renderObjects.length; i < il; ++i) {
|
||||
await ctx.update({ message: `Exporting object ${i}/${il}` });
|
||||
await objExporter.add(renderObjects[i], ctx);
|
||||
}
|
||||
const { obj, mtl } = objExporter.getData();
|
||||
|
||||
const asciiWrite = (data: Uint8Array, str: string) => {
|
||||
for (let i = 0, il = str.length; i < il; ++i) {
|
||||
data[i] = str.charCodeAt(i);
|
||||
}
|
||||
};
|
||||
const objData = new Uint8Array(obj.length);
|
||||
asciiWrite(objData, obj);
|
||||
const mtlData = new Uint8Array(mtl.length);
|
||||
asciiWrite(mtlData, mtl);
|
||||
|
||||
const zipDataObj = {
|
||||
[filename + '.obj']: objData,
|
||||
[filename + '.mtl']: mtlData
|
||||
};
|
||||
const zipData = await zip(ctx, zipDataObj);
|
||||
return {
|
||||
zipData,
|
||||
filename: filename + '.zip'
|
||||
};
|
||||
} catch (e) {
|
||||
this.plugin.log.error('' + e);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
return this.plugin.runTask(task, { useOverlay: true });
|
||||
}
|
||||
|
||||
constructor(private plugin: PluginContext) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
321
src/extensions/geo-export/export.ts
Normal file
321
src/extensions/geo-export/export.ts
Normal file
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
*/
|
||||
|
||||
import { GraphicsRenderObject } from '../../mol-gl/render-object';
|
||||
import { MeshValues } from '../../mol-gl/renderable/mesh';
|
||||
import { LinesValues } from '../../mol-gl/renderable/lines';
|
||||
import { PointsValues } from '../../mol-gl/renderable/points';
|
||||
import { SpheresValues } from '../../mol-gl/renderable/spheres';
|
||||
import { CylindersValues } from '../../mol-gl/renderable/cylinders';
|
||||
import { BaseValues, SizeValues } from '../../mol-gl/renderable/schema';
|
||||
import { TextureImage } from '../../mol-gl/renderable/util';
|
||||
import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
|
||||
import { addSphere } from '../../mol-geo/geometry/mesh/builder/sphere';
|
||||
import { addCylinder } from '../../mol-geo/geometry/mesh/builder/cylinder';
|
||||
import { Vec3, Mat3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
import { Color } from '../../mol-util/color/color';
|
||||
import { decodeFloatRGB } from '../../mol-util/float-packing';
|
||||
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3fromArray = Vec3.fromArray;
|
||||
const v3transformMat4 = Vec3.transformMat4;
|
||||
const v3transformMat3 = Vec3.transformMat3;
|
||||
const mat3directionTransform = Mat3.directionTransform;
|
||||
|
||||
type RenderObjectExportData = {
|
||||
[k: string]: string | Uint8Array | undefined
|
||||
}
|
||||
|
||||
interface RenderObjectExporter<D extends RenderObjectExportData> {
|
||||
add(renderObject: GraphicsRenderObject, ctx: RuntimeContext): Promise<void> | undefined
|
||||
getData(): D
|
||||
}
|
||||
|
||||
// http://paulbourke.net/dataformats/obj/
|
||||
// http://paulbourke.net/dataformats/mtl/
|
||||
|
||||
export type ObjData = {
|
||||
obj: string
|
||||
mtl: string
|
||||
}
|
||||
|
||||
export class ObjExporter implements RenderObjectExporter<ObjData> {
|
||||
private obj = StringBuilder.create();
|
||||
private mtl = StringBuilder.create();
|
||||
private vertexOffset = 0;
|
||||
private currentColor: Color | undefined;
|
||||
private currentAlpha: number | undefined;
|
||||
private materialSet = new Set<string>();
|
||||
|
||||
private static getSizeFromTexture(tSize: TextureImage<Uint8Array>, i: number): number {
|
||||
const r = tSize.array[i * 3];
|
||||
const g = tSize.array[i * 3 + 1];
|
||||
const b = tSize.array[i * 3 + 2];
|
||||
return decodeFloatRGB(r, g, b);
|
||||
}
|
||||
|
||||
private static getSize(values: BaseValues & SizeValues, instanceIndex: number, group: number): number {
|
||||
const tSize = values.tSize.ref.value;
|
||||
let size = 0;
|
||||
switch (values.dSizeType.ref.value) {
|
||||
case 'uniform':
|
||||
size = values.uSize.ref.value;
|
||||
break;
|
||||
case 'instance':
|
||||
size = ObjExporter.getSizeFromTexture(tSize, instanceIndex) / 100;
|
||||
break;
|
||||
case 'group':
|
||||
size = ObjExporter.getSizeFromTexture(tSize, group) / 100;
|
||||
break;
|
||||
case 'groupInstance':
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
size = ObjExporter.getSizeFromTexture(tSize, instanceIndex * groupCount + group) / 100;
|
||||
break;
|
||||
}
|
||||
return size * values.uSizeFactor.ref.value;
|
||||
}
|
||||
|
||||
private updateMaterial(color: Color, alpha: number) {
|
||||
if (this.currentColor === color && this.currentAlpha === alpha) return;
|
||||
|
||||
this.currentColor = color;
|
||||
this.currentAlpha = alpha;
|
||||
const material = Color.toHexString(color) + alpha;
|
||||
StringBuilder.writeSafe(this.obj, `usemtl ${material}`);
|
||||
StringBuilder.newline(this.obj);
|
||||
if (!this.materialSet.has(material)) {
|
||||
this.materialSet.add(material);
|
||||
const [r, g, b] = Color.toRgbNormalized(color);
|
||||
const mtl = this.mtl;
|
||||
StringBuilder.writeSafe(mtl, `newmtl ${material}\n`);
|
||||
StringBuilder.writeSafe(mtl, 'illum 2\n'); // illumination model
|
||||
StringBuilder.writeSafe(mtl, 'Ns 163\n'); // specular exponent
|
||||
StringBuilder.writeSafe(mtl, 'Ni 0.001\n'); // optical density a.k.a. index of refraction
|
||||
StringBuilder.writeSafe(mtl, 'Ka 0 0 0\n'); // ambient reflectivity
|
||||
StringBuilder.writeSafe(mtl, 'Kd '); // diffuse reflectivity
|
||||
StringBuilder.writeFloat(mtl, r, 1000);
|
||||
StringBuilder.whitespace1(mtl);
|
||||
StringBuilder.writeFloat(mtl, g, 1000);
|
||||
StringBuilder.whitespace1(mtl);
|
||||
StringBuilder.writeFloat(mtl, b, 1000);
|
||||
StringBuilder.newline(mtl);
|
||||
StringBuilder.writeSafe(mtl, 'Ks 0.25 0.25 0.25\n'); // specular reflectivity
|
||||
StringBuilder.writeSafe(mtl, 'd '); // dissolve
|
||||
StringBuilder.writeFloat(mtl, alpha, 1000);
|
||||
StringBuilder.newline(mtl);
|
||||
}
|
||||
}
|
||||
|
||||
private async addMeshWithColors(vertices: Float32Array, normals: Float32Array, indices: Uint32Array, groups: Float32Array, vertexCount: number, drawCount: number, values: BaseValues, instanceIndex: number, ctx: RuntimeContext) {
|
||||
const obj = this.obj;
|
||||
const t = Mat4();
|
||||
const n = Mat3();
|
||||
const tmpV = Vec3();
|
||||
|
||||
const colorType = values.dColorType.ref.value;
|
||||
const tColor = values.tColor.ref.value.array;
|
||||
const uAlpha = values.uAlpha.ref.value;
|
||||
const aTransform = values.aTransform.ref.value;
|
||||
|
||||
Mat4.fromArray(t, aTransform, instanceIndex * 16);
|
||||
mat3directionTransform(n, t);
|
||||
|
||||
const currentProgress = (vertexCount * 2 + drawCount) * instanceIndex;
|
||||
await ctx.update({ isIndeterminate: false, current: currentProgress, max: (vertexCount * 2 + drawCount) * values.uInstanceCount.ref.value });
|
||||
|
||||
// position
|
||||
for (let i = 0; i < vertexCount; ++i) {
|
||||
if (i % 1000 === 0 && ctx.shouldUpdate) await ctx.update({ current: currentProgress + i });
|
||||
v3transformMat4(tmpV, v3fromArray(tmpV, vertices, i * 3), t);
|
||||
StringBuilder.writeSafe(obj, 'v ');
|
||||
StringBuilder.writeFloat(obj, tmpV[0], 1000);
|
||||
StringBuilder.whitespace1(obj);
|
||||
StringBuilder.writeFloat(obj, tmpV[1], 1000);
|
||||
StringBuilder.whitespace1(obj);
|
||||
StringBuilder.writeFloat(obj, tmpV[2], 1000);
|
||||
StringBuilder.newline(obj);
|
||||
}
|
||||
|
||||
// normal
|
||||
for (let i = 0; i < vertexCount; ++i) {
|
||||
if (i % 1000 === 0 && ctx.shouldUpdate) await ctx.update({ current: currentProgress + vertexCount + i });
|
||||
v3transformMat3(tmpV, v3fromArray(tmpV, normals, i * 3), n);
|
||||
StringBuilder.writeSafe(obj, 'vn ');
|
||||
StringBuilder.writeFloat(obj, tmpV[0], 100);
|
||||
StringBuilder.whitespace1(obj);
|
||||
StringBuilder.writeFloat(obj, tmpV[1], 100);
|
||||
StringBuilder.whitespace1(obj);
|
||||
StringBuilder.writeFloat(obj, tmpV[2], 100);
|
||||
StringBuilder.newline(obj);
|
||||
}
|
||||
|
||||
// face
|
||||
for (let i = 0; i < drawCount; i += 3) {
|
||||
if (i % 3000 === 0 && ctx.shouldUpdate) await ctx.update({ current: currentProgress + vertexCount * 2 + i });
|
||||
let color: Color;
|
||||
switch (colorType) {
|
||||
case 'uniform':
|
||||
color = Color.fromNormalizedArray(values.uColor.ref.value, 0);
|
||||
break;
|
||||
case 'instance':
|
||||
color = Color.fromArray(tColor, instanceIndex * 3);
|
||||
break;
|
||||
case 'group':
|
||||
color = Color.fromArray(tColor, groups[indices[i]] * 3);
|
||||
break;
|
||||
case 'groupInstance':
|
||||
const groupCount = values.uGroupCount.ref.value;
|
||||
const group = groups[indices[i]];
|
||||
color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
|
||||
break;
|
||||
case 'vertex':
|
||||
color = Color.fromArray(tColor, i * 3);
|
||||
break;
|
||||
case 'vertexInstance':
|
||||
color = Color.fromArray(tColor, (instanceIndex * drawCount + i) * 3);
|
||||
break;
|
||||
default: throw new Error('Unsupported color type.');
|
||||
}
|
||||
this.updateMaterial(color, uAlpha);
|
||||
|
||||
const v1 = this.vertexOffset + indices[i] + 1;
|
||||
const v2 = this.vertexOffset + indices[i + 1] + 1;
|
||||
const v3 = this.vertexOffset + indices[i + 2] + 1;
|
||||
StringBuilder.writeSafe(obj, 'f ');
|
||||
StringBuilder.writeInteger(obj, v1);
|
||||
StringBuilder.writeSafe(obj, '//');
|
||||
StringBuilder.writeIntegerAndSpace(obj, v1);
|
||||
StringBuilder.writeInteger(obj, v2);
|
||||
StringBuilder.writeSafe(obj, '//');
|
||||
StringBuilder.writeIntegerAndSpace(obj, v2);
|
||||
StringBuilder.writeInteger(obj, v3);
|
||||
StringBuilder.writeSafe(obj, '//');
|
||||
StringBuilder.writeInteger(obj, v3);
|
||||
StringBuilder.newline(obj);
|
||||
}
|
||||
|
||||
this.vertexOffset += vertexCount;
|
||||
}
|
||||
|
||||
private async addMesh(values: MeshValues, ctx: RuntimeContext) {
|
||||
const aPosition = values.aPosition.ref.value;
|
||||
const aNormal = values.aNormal.ref.value;
|
||||
const elements = values.elements.ref.value;
|
||||
const aGroup = values.aGroup.ref.value;
|
||||
const instanceCount = values.instanceCount.ref.value;
|
||||
const vertexCount = values.uVertexCount.ref.value;
|
||||
const drawCount = values.drawCount.ref.value;
|
||||
|
||||
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
|
||||
await this.addMeshWithColors(aPosition, aNormal, elements, aGroup, vertexCount, drawCount, values, instanceIndex, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private async addLines(values: LinesValues, ctx: RuntimeContext) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private async addPoints(values: PointsValues, ctx: RuntimeContext) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private async addSpheres(values: SpheresValues, ctx: RuntimeContext) {
|
||||
const center = Vec3();
|
||||
|
||||
const aPosition = values.aPosition.ref.value;
|
||||
const aGroup = values.aGroup.ref.value;
|
||||
const instanceCount = values.instanceCount.ref.value;
|
||||
const vertexCount = values.uVertexCount.ref.value;
|
||||
|
||||
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
|
||||
const state = MeshBuilder.createState(512, 256);
|
||||
|
||||
for (let i = 0; i < vertexCount; i += 4) {
|
||||
v3fromArray(center, aPosition, i * 3);
|
||||
|
||||
const group = aGroup[i];
|
||||
const radius = ObjExporter.getSize(values, instanceIndex, group);
|
||||
state.currentGroup = group;
|
||||
addSphere(state, center, radius, 2);
|
||||
}
|
||||
|
||||
const mesh = MeshBuilder.getMesh(state);
|
||||
const vertices = mesh.vertexBuffer.ref.value;
|
||||
const normals = mesh.normalBuffer.ref.value;
|
||||
const indices = mesh.indexBuffer.ref.value;
|
||||
const groups = mesh.groupBuffer.ref.value;
|
||||
await this.addMeshWithColors(vertices, normals, indices, groups, vertices.length / 3, indices.length, values, instanceIndex, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private async addCylinders(values: CylindersValues, ctx: RuntimeContext) {
|
||||
const start = Vec3();
|
||||
const end = Vec3();
|
||||
|
||||
const aStart = values.aStart.ref.value;
|
||||
const aEnd = values.aEnd.ref.value;
|
||||
const aScale = values.aScale.ref.value;
|
||||
const aCap = values.aCap.ref.value;
|
||||
const aGroup = values.aGroup.ref.value;
|
||||
const instanceCount = values.instanceCount.ref.value;
|
||||
const vertexCount = values.uVertexCount.ref.value;
|
||||
|
||||
for (let instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
|
||||
const state = MeshBuilder.createState(512, 256);
|
||||
|
||||
for (let i = 0; i < vertexCount; i += 6) {
|
||||
v3fromArray(start, aStart, i * 3);
|
||||
v3fromArray(end, aEnd, i * 3);
|
||||
|
||||
const group = aGroup[i];
|
||||
const radius = ObjExporter.getSize(values, instanceIndex, group) * aScale[i];
|
||||
const cap = aCap[i];
|
||||
const topCap = cap === 1 || cap === 3;
|
||||
const bottomCap = cap >= 2;
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius, topCap, bottomCap, radialSegments: 32 };
|
||||
state.currentGroup = aGroup[i];
|
||||
addCylinder(state, start, end, 1, cylinderProps);
|
||||
}
|
||||
|
||||
const mesh = MeshBuilder.getMesh(state);
|
||||
const vertices = mesh.vertexBuffer.ref.value;
|
||||
const normals = mesh.normalBuffer.ref.value;
|
||||
const indices = mesh.indexBuffer.ref.value;
|
||||
const groups = mesh.groupBuffer.ref.value;
|
||||
await this.addMeshWithColors(vertices, normals, indices, groups, vertices.length / 3, indices.length, values, instanceIndex, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
add(renderObject: GraphicsRenderObject, ctx: RuntimeContext) {
|
||||
if (!renderObject.state.visible) return;
|
||||
|
||||
switch (renderObject.type) {
|
||||
case 'mesh':
|
||||
return this.addMesh(renderObject.values as MeshValues, ctx);
|
||||
case 'lines':
|
||||
return this.addLines(renderObject.values as LinesValues, ctx);
|
||||
case 'points':
|
||||
return this.addPoints(renderObject.values as PointsValues, ctx);
|
||||
case 'spheres':
|
||||
return this.addSpheres(renderObject.values as SpheresValues, ctx);
|
||||
case 'cylinders':
|
||||
return this.addCylinders(renderObject.values as CylindersValues, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
getData() {
|
||||
return {
|
||||
obj: StringBuilder.getString(this.obj),
|
||||
mtl: StringBuilder.getString(this.mtl)
|
||||
};
|
||||
}
|
||||
|
||||
constructor(filename: string) {
|
||||
StringBuilder.writeSafe(this.obj, `mtllib ${filename}.mtl\n`);
|
||||
}
|
||||
}
|
||||
30
src/extensions/geo-export/index.ts
Normal file
30
src/extensions/geo-export/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
*/
|
||||
|
||||
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
|
||||
import { GeometryExporterUI } from './ui';
|
||||
|
||||
export const GeometryExport = PluginBehavior.create<{ }>({
|
||||
name: 'extension-geo-export',
|
||||
category: 'misc',
|
||||
display: {
|
||||
name: 'Geometry Export'
|
||||
},
|
||||
ctor: class extends PluginBehavior.Handler<{ }> {
|
||||
register(): void {
|
||||
this.ctx.customStructureControls.set('geo-export', GeometryExporterUI as any);
|
||||
}
|
||||
|
||||
update() {
|
||||
return false;
|
||||
}
|
||||
|
||||
unregister() {
|
||||
this.ctx.customStructureControls.delete('geo-export');
|
||||
}
|
||||
},
|
||||
params: () => ({ })
|
||||
});
|
||||
64
src/extensions/geo-export/ui.tsx
Normal file
64
src/extensions/geo-export/ui.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sukolsak Sakshuwong <sukolsak@stanford.edu>
|
||||
*/
|
||||
|
||||
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
|
||||
import { Button } from '../../mol-plugin-ui/controls/common';
|
||||
import { GetAppSvg, CubeSendSvg } from '../../mol-plugin-ui/controls/icons';
|
||||
import { download } from '../../mol-util/download';
|
||||
import { GeometryControls } from './controls';
|
||||
|
||||
interface State {
|
||||
busy?: boolean
|
||||
}
|
||||
|
||||
export class GeometryExporterUI extends CollapsableControls<{}, State> {
|
||||
private _controls: GeometryControls | undefined;
|
||||
|
||||
get controls() {
|
||||
return this._controls || (this._controls = new GeometryControls(this.plugin));
|
||||
}
|
||||
|
||||
protected defaultState(): State & CollapsableState {
|
||||
return {
|
||||
header: 'Export Geometries',
|
||||
isCollapsed: true,
|
||||
brand: { accent: 'cyan', svg: CubeSendSvg }
|
||||
};
|
||||
}
|
||||
|
||||
protected renderControls(): JSX.Element {
|
||||
return <>
|
||||
<Button icon={GetAppSvg}
|
||||
onClick={this.saveObj} style={{ marginTop: 1 }}
|
||||
disabled={this.state.busy || !this.plugin.canvas3d?.reprCount.value}>
|
||||
Save OBJ + MTL
|
||||
</Button>
|
||||
</>;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.subscribe(this.plugin.canvas3d!.reprCount, () => {
|
||||
if (!this.state.isCollapsed) this.forceUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._controls?.dispose();
|
||||
this._controls = void 0;
|
||||
}
|
||||
|
||||
saveObj = async () => {
|
||||
try {
|
||||
this.setState({ busy: true });
|
||||
const data = await this.controls.exportObj();
|
||||
this.setState({ busy: false });
|
||||
|
||||
download(new Blob([data.zipData]), data.filename);
|
||||
} catch {
|
||||
this.setState({ busy: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,24 @@ export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 've
|
||||
export type ColorData = {
|
||||
uColor: ValueCell<Vec3>,
|
||||
tColor: ValueCell<TextureImage<Uint8Array>>,
|
||||
tPalette: ValueCell<TextureImage<Uint8Array>>,
|
||||
uColorTexDim: ValueCell<Vec2>,
|
||||
dColorType: ValueCell<string>,
|
||||
dUsePalette: ValueCell<boolean>,
|
||||
}
|
||||
|
||||
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
|
||||
const data = _createColors(locationIt, positionIt, colorTheme, colorData);
|
||||
if (colorTheme.palette) {
|
||||
ValueCell.updateIfChanged(data.dUsePalette, true);
|
||||
updatePaletteTexture(colorTheme.palette, data.tPalette);
|
||||
} else {
|
||||
ValueCell.updateIfChanged(data.dUsePalette, false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
|
||||
switch (Geometry.getGranularity(locationIt, colorTheme.granularity)) {
|
||||
case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData);
|
||||
case 'instance': return createInstanceColor(locationIt, colorTheme.color, colorData);
|
||||
@@ -42,18 +55,20 @@ export function createValueColor(value: Color, colorData?: ColorData): ColorData
|
||||
return {
|
||||
uColor: ValueCell.create(Color.toVec3Normalized(Vec3(), value)),
|
||||
tColor: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
|
||||
tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
|
||||
uColorTexDim: ValueCell.create(Vec2.create(1, 1)),
|
||||
dColorType: ValueCell.create('uniform'),
|
||||
dUsePalette: ValueCell.create(false),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates color uniform */
|
||||
export function createUniformColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
function createUniformColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
return createValueColor(color(NullLocation, false), colorData);
|
||||
}
|
||||
|
||||
export function createTextureColor(colors: TextureImage<Uint8Array>, type: ColorType, colorData?: ColorData): ColorData {
|
||||
function createTextureColor(colors: TextureImage<Uint8Array>, type: ColorType, colorData?: ColorData): ColorData {
|
||||
if (colorData) {
|
||||
ValueCell.update(colorData.tColor, colors);
|
||||
ValueCell.update(colorData.uColorTexDim, Vec2.create(colors.width, colors.height));
|
||||
@@ -63,14 +78,16 @@ export function createTextureColor(colors: TextureImage<Uint8Array>, type: Color
|
||||
return {
|
||||
uColor: ValueCell.create(Vec3()),
|
||||
tColor: ValueCell.create(colors),
|
||||
tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
|
||||
uColorTexDim: ValueCell.create(Vec2.create(colors.width, colors.height)),
|
||||
dColorType: ValueCell.create(type),
|
||||
dUsePalette: ValueCell.create(false),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates color texture with color for each instance */
|
||||
export function createInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
function createInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
const { instanceCount } = locationIt;
|
||||
const colors = createTextureImage(Math.max(1, instanceCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
|
||||
locationIt.reset();
|
||||
@@ -83,7 +100,7 @@ export function createInstanceColor(locationIt: LocationIterator, color: Locatio
|
||||
}
|
||||
|
||||
/** Creates color texture with color for each group (i.e. shared across instances) */
|
||||
export function createGroupColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
function createGroupColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
const { groupCount } = locationIt;
|
||||
const colors = createTextureImage(Math.max(1, groupCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
|
||||
locationIt.reset();
|
||||
@@ -95,7 +112,7 @@ export function createGroupColor(locationIt: LocationIterator, color: LocationCo
|
||||
}
|
||||
|
||||
/** Creates color texture with color for each group in each instance */
|
||||
export function createGroupInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
function createGroupInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
const { groupCount, instanceCount } = locationIt;
|
||||
const count = instanceCount * groupCount;
|
||||
const colors = createTextureImage(Math.max(1, count), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
|
||||
@@ -108,7 +125,7 @@ export function createGroupInstanceColor(locationIt: LocationIterator, color: Lo
|
||||
}
|
||||
|
||||
/** Creates color texture with color for each vertex (i.e. shared across instances) */
|
||||
export function createVertexColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
function createVertexColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
const { groupCount, stride } = locationIt;
|
||||
const colors = createTextureImage(Math.max(1, groupCount), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
|
||||
locationIt.reset();
|
||||
@@ -124,7 +141,7 @@ export function createVertexColor(locationIt: LocationIterator, color: LocationC
|
||||
}
|
||||
|
||||
/** Creates color texture with color for each vertex in each instance */
|
||||
export function createVertexInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
function createVertexInstanceColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
|
||||
const { groupCount, instanceCount, stride } = locationIt;
|
||||
const count = instanceCount * groupCount;
|
||||
const colors = createTextureImage(Math.max(1, count), 3, Uint8Array, colorData && colorData.tColor.ref.value.array);
|
||||
@@ -138,3 +155,34 @@ export function createVertexInstanceColor(locationIt: LocationIterator, color: L
|
||||
}
|
||||
return createTextureColor(colors, 'vertexInstance', colorData);
|
||||
}
|
||||
|
||||
function updatePaletteTexture(palette: ColorTheme.Palette, cell: ValueCell<TextureImage<Uint8Array>>) {
|
||||
let isSynced = true;
|
||||
const texture = cell.ref.value;
|
||||
if (palette.colors.length !== texture.width || texture.filter !== palette.filter) {
|
||||
isSynced = false;
|
||||
} else {
|
||||
const data = texture.array;
|
||||
let o = 0;
|
||||
for (const c of palette.colors) {
|
||||
const [r, g, b] = Color.toRgb(c);
|
||||
if (data[o++] !== r || data[o++] !== g || data[o++] !== b) {
|
||||
isSynced = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isSynced) return;
|
||||
|
||||
const array = new Uint8Array(palette.colors.length * 3);
|
||||
let o = 0;
|
||||
for (const c of palette.colors) {
|
||||
const [r, g, b] = Color.toRgb(c);
|
||||
array[o++] = r;
|
||||
array[o++] = g;
|
||||
array[o++] = b;
|
||||
}
|
||||
|
||||
ValueCell.update(cell, { array, height: 1, width: palette.colors.length, filter: palette.filter });
|
||||
}
|
||||
@@ -133,7 +133,7 @@ describe('renderer', () => {
|
||||
scene.add(points);
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(6);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(7);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(6);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(6);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(12);
|
||||
|
||||
@@ -185,7 +185,9 @@ export const ColorSchema = {
|
||||
uColor: UniformSpec('v3', 'material'),
|
||||
uColorTexDim: UniformSpec('v2'),
|
||||
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
|
||||
tPalette: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
|
||||
dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance']),
|
||||
dUsePalette: DefineSpec('boolean'),
|
||||
} as const;
|
||||
export type ColorSchema = typeof ColorSchema
|
||||
export type ColorValues = Values<ColorSchema>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { Sphere3D } from '../../mol-math/geometry';
|
||||
import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
|
||||
import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
|
||||
import { TextureFilter } from '../webgl/texture';
|
||||
|
||||
export function calculateTextureInfo (n: number, itemSize: number) {
|
||||
n = Math.max(n, 2); // observed issues with 1 pixel textures
|
||||
@@ -22,6 +23,7 @@ export interface TextureImage<T extends Uint8Array | Float32Array | Int32Array>
|
||||
readonly width: number
|
||||
readonly height: number
|
||||
readonly flipY?: boolean
|
||||
readonly filter?: TextureFilter
|
||||
}
|
||||
|
||||
export interface TextureVolume<T extends Uint8Array | Float32Array> {
|
||||
|
||||
@@ -14,6 +14,10 @@ export const assign_color_varying = `
|
||||
vColor.rgb = readFromTexture(tColor, int(aInstance) * uVertexCount + VertexID, uColorTexDim).rgb;
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
vPaletteV = ((vColor.r * 256.0 * 256.0 * 255.0 + vColor.g * 256.0 * 255.0 + vColor.b * 255.0) - 1.0) / 16777215.0;
|
||||
#endif
|
||||
|
||||
#ifdef dOverpaint
|
||||
vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export const assign_material_color = `
|
||||
#if defined(dRenderVariant_color)
|
||||
#if defined(dColorType_uniform)
|
||||
#if defined(dUsePalette)
|
||||
vec4 material = vec4(texture2D(tPalette, vec2(vPaletteV, 0.5)).rgb, uAlpha);
|
||||
#elif defined(dColorType_uniform)
|
||||
vec4 material = vec4(uColor, uAlpha);
|
||||
#elif defined(dColorType_varying)
|
||||
vec4 material = vec4(vColor.rgb, uAlpha);
|
||||
|
||||
@@ -21,4 +21,9 @@ export const color_frag_params = `
|
||||
varying float vGroup;
|
||||
varying float vTransparency;
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
uniform sampler2D tPalette;
|
||||
varying float vPaletteV;
|
||||
#endif
|
||||
`;
|
||||
@@ -30,4 +30,8 @@ export const color_vert_params = `
|
||||
uniform vec2 uTransparencyTexDim;
|
||||
uniform sampler2D tTransparency;
|
||||
#endif
|
||||
|
||||
#ifdef dUsePalette
|
||||
varying float vPaletteV;
|
||||
#endif
|
||||
`;
|
||||
@@ -283,6 +283,9 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, type, data);
|
||||
} else if (isTexture2d(data, target, gl)) {
|
||||
const _filter = data.filter ? getFilter(gl, data.filter) : filter;
|
||||
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, _filter);
|
||||
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, _filter);
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);
|
||||
if (sub) {
|
||||
gl.texSubImage2D(target, 0, 0, 0, data.width, data.height, format, type, data.array);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { parseSdf } from '../sdf/parser';
|
||||
|
||||
const SdfString = `
|
||||
Mrv1718007121815122D
|
||||
Mrv1718007121815122D
|
||||
|
||||
5 4 0 0 0 0 999 V2000
|
||||
0.0000 0.8250 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0
|
||||
@@ -129,7 +129,208 @@ Comp 2
|
||||
M CHG 3 1 -1 3 -1 5 -1
|
||||
M END
|
||||
> <DATABASE_ID>
|
||||
1`;
|
||||
1
|
||||
|
||||
$$$$
|
||||
|
||||
2244
|
||||
-OEChem-04122119123D
|
||||
|
||||
21 21 0 0 0 0 0 0 0999 V2000
|
||||
1.2333 0.5540 0.7792 O 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.6952 -2.7148 -0.7502 O 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
0.7958 -2.1843 0.8685 O 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
1.7813 0.8105 -1.4821 O 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.0857 0.6088 0.4403 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.7927 -0.5515 0.1244 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.7288 1.8464 0.4133 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-2.1426 -0.4741 -0.2184 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-2.0787 1.9238 0.0706 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-2.7855 0.7636 -0.2453 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.1409 -1.8536 0.1477 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
2.1094 0.6715 -0.3113 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3.5305 0.5996 0.1635 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.1851 2.7545 0.6593 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-2.7247 -1.3605 -0.4564 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-2.5797 2.8872 0.0506 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-3.8374 0.8238 -0.5090 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3.7290 1.4184 0.8593 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
4.2045 0.6969 -0.6924 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
3.7105 -0.3659 0.6426 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.2555 -3.5916 -0.7337 H 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
1 5 1 0 0 0 0
|
||||
1 12 1 0 0 0 0
|
||||
2 11 1 0 0 0 0
|
||||
2 21 1 0 0 0 0
|
||||
3 11 2 0 0 0 0
|
||||
4 12 2 0 0 0 0
|
||||
5 6 1 0 0 0 0
|
||||
5 7 2 0 0 0 0
|
||||
6 8 2 0 0 0 0
|
||||
6 11 1 0 0 0 0
|
||||
7 9 1 0 0 0 0
|
||||
7 14 1 0 0 0 0
|
||||
8 10 1 0 0 0 0
|
||||
8 15 1 0 0 0 0
|
||||
9 10 2 0 0 0 0
|
||||
9 16 1 0 0 0 0
|
||||
10 17 1 0 0 0 0
|
||||
12 13 1 0 0 0 0
|
||||
13 18 1 0 0 0 0
|
||||
13 19 1 0 0 0 0
|
||||
13 20 1 0 0 0 0
|
||||
M END
|
||||
> <PUBCHEM_COMPOUND_CID>
|
||||
2244
|
||||
|
||||
> <PUBCHEM_CONFORMER_RMSD>
|
||||
0.6
|
||||
|
||||
> <PUBCHEM_CONFORMER_DIVERSEORDER>
|
||||
1
|
||||
11
|
||||
10
|
||||
3
|
||||
15
|
||||
17
|
||||
13
|
||||
5
|
||||
16
|
||||
7
|
||||
14
|
||||
9
|
||||
8
|
||||
4
|
||||
18
|
||||
6
|
||||
12
|
||||
2
|
||||
|
||||
> <PUBCHEM_MMFF94_PARTIAL_CHARGES>
|
||||
18
|
||||
1 -0.23
|
||||
10 -0.15
|
||||
11 0.63
|
||||
12 0.66
|
||||
13 0.06
|
||||
14 0.15
|
||||
15 0.15
|
||||
16 0.15
|
||||
17 0.15
|
||||
2 -0.65
|
||||
21 0.5
|
||||
3 -0.57
|
||||
4 -0.57
|
||||
5 0.08
|
||||
6 0.09
|
||||
7 -0.15
|
||||
8 -0.15
|
||||
9 -0.15
|
||||
|
||||
> <PUBCHEM_EFFECTIVE_ROTOR_COUNT>
|
||||
3
|
||||
|
||||
> <PUBCHEM_PHARMACOPHORE_FEATURES>
|
||||
5
|
||||
1 2 acceptor
|
||||
1 3 acceptor
|
||||
1 4 acceptor
|
||||
3 2 3 11 anion
|
||||
6 5 6 7 8 9 10 rings
|
||||
|
||||
> <PUBCHEM_HEAVY_ATOM_COUNT>
|
||||
13
|
||||
|
||||
> <PUBCHEM_ATOM_DEF_STEREO_COUNT>
|
||||
0
|
||||
|
||||
> <PUBCHEM_ATOM_UDEF_STEREO_COUNT>
|
||||
0
|
||||
|
||||
> <PUBCHEM_BOND_DEF_STEREO_COUNT>
|
||||
0
|
||||
|
||||
> <PUBCHEM_BOND_UDEF_STEREO_COUNT>
|
||||
0
|
||||
|
||||
> <PUBCHEM_ISOTOPIC_ATOM_COUNT>
|
||||
0
|
||||
|
||||
> <PUBCHEM_COMPONENT_COUNT>
|
||||
1
|
||||
|
||||
> <PUBCHEM_CACTVS_TAUTO_COUNT>
|
||||
1
|
||||
|
||||
> <PUBCHEM_CONFORMER_ID>
|
||||
000008C400000001
|
||||
|
||||
> <PUBCHEM_MMFF94_ENERGY>
|
||||
39.5952
|
||||
|
||||
> <PUBCHEM_FEATURE_SELFOVERLAP>
|
||||
25.432
|
||||
|
||||
> <PUBCHEM_SHAPE_FINGERPRINT>
|
||||
1 1 18265615372930943622
|
||||
100427 49 16967750034970055351
|
||||
12138202 97 18271247217817981012
|
||||
12423570 1 16692715976000295083
|
||||
12524768 44 16753525617747228747
|
||||
12716758 59 18341332292274886536
|
||||
13024252 1 17968377969333732145
|
||||
14181834 199 17830728755827362645
|
||||
14614273 12 18262232214645093005
|
||||
15207287 21 17703787037639964108
|
||||
15775835 57 18340488876329928641
|
||||
16945 1 18271533103414939405
|
||||
193761 8 17907860604865584321
|
||||
20645476 183 17677348215414174190
|
||||
20871998 184 18198632231250704846
|
||||
21040471 1 18411412921197846465
|
||||
21501502 16 18123463883164380929
|
||||
23402539 116 18271795865171824860
|
||||
23419403 2 13539898140662769886
|
||||
23552423 10 18048876295495619569
|
||||
23559900 14 18272369794190581304
|
||||
241688 4 16179044415907240795
|
||||
257057 1 17478316999871287486
|
||||
2748010 2 18339085878070479087
|
||||
305870 269 18263645056784260212
|
||||
528862 383 18117272558388284091
|
||||
53812653 8 18410289211719108569
|
||||
7364860 26 17910392788380644719
|
||||
81228 2 18050568744116491203
|
||||
|
||||
> <PUBCHEM_SHAPE_MULTIPOLES>
|
||||
244.06
|
||||
3.86
|
||||
2.45
|
||||
0.89
|
||||
1.95
|
||||
1.58
|
||||
0.15
|
||||
-1.85
|
||||
0.38
|
||||
-0.61
|
||||
-0.02
|
||||
0.29
|
||||
0.01
|
||||
-0.33
|
||||
|
||||
> <PUBCHEM_SHAPE_SELFOVERLAP>
|
||||
513.037
|
||||
|
||||
> <PUBCHEM_SHAPE_VOLUME>
|
||||
136
|
||||
|
||||
> <PUBCHEM_COORDINATE_TYPE>
|
||||
2
|
||||
5
|
||||
10
|
||||
|
||||
$$$$
|
||||
`;
|
||||
|
||||
describe('sdf reader', () => {
|
||||
it('basic', async () => {
|
||||
@@ -139,10 +340,11 @@ describe('sdf reader', () => {
|
||||
}
|
||||
const compound1 = parsed.result.compounds[0];
|
||||
const compound2 = parsed.result.compounds[1];
|
||||
const compound3 = parsed.result.compounds[2];
|
||||
const { molFile, dataItems } = compound1;
|
||||
const { atoms, bonds } = molFile;
|
||||
|
||||
expect(parsed.result.compounds.length).toBe(2);
|
||||
expect(parsed.result.compounds.length).toBe(3);
|
||||
|
||||
// number of structures
|
||||
expect(atoms.count).toBe(5);
|
||||
@@ -171,5 +373,11 @@ describe('sdf reader', () => {
|
||||
|
||||
expect(compound1.dataItems.data.value(0)).toBe('0');
|
||||
expect(compound2.dataItems.data.value(0)).toBe('1');
|
||||
|
||||
expect(compound3.dataItems.dataHeader.value(2)).toBe('PUBCHEM_CONFORMER_DIVERSEORDER');
|
||||
expect(compound3.dataItems.data.value(2)).toBe('1\n11\n10\n3\n15\n17\n13\n5\n16\n7\n14\n9\n8\n4\n18\n6\n12\n2');
|
||||
|
||||
expect(compound3.dataItems.dataHeader.value(21)).toBe('PUBCHEM_COORDINATE_TYPE');
|
||||
expect(compound3.dataItems.data.value(21)).toBe('2\n5\n10');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Column } from '../../../mol-data/db';
|
||||
@@ -27,21 +28,31 @@ function handleDataItems(tokenizer: Tokenizer): { dataHeader: Column<string>, da
|
||||
const dataHeader = TokenBuilder.create(tokenizer.data, 32);
|
||||
const data = TokenBuilder.create(tokenizer.data, 32);
|
||||
|
||||
let sawHeaderToken = false;
|
||||
while (tokenizer.position < tokenizer.length) {
|
||||
const line = Tokenizer.readLine(tokenizer);
|
||||
if (line.startsWith(delimiter)) break;
|
||||
if (!!line) {
|
||||
if (line.startsWith('> <')) {
|
||||
TokenBuilder.add(dataHeader, tokenizer.tokenStart + 3, tokenizer.tokenEnd - 1);
|
||||
sawHeaderToken = true;
|
||||
} else if (sawHeaderToken) {
|
||||
TokenBuilder.add(data, tokenizer.tokenStart, tokenizer.tokenEnd);
|
||||
sawHeaderToken = false;
|
||||
// TODO can there be multiline values?
|
||||
if (!line) continue;
|
||||
|
||||
if (line.startsWith('> <')) {
|
||||
TokenBuilder.add(dataHeader, tokenizer.tokenStart + 3, tokenizer.tokenEnd - 1);
|
||||
|
||||
Tokenizer.markLine(tokenizer);
|
||||
const start = tokenizer.tokenStart;
|
||||
let end = tokenizer.tokenEnd;
|
||||
let added = false;
|
||||
while (tokenizer.position < tokenizer.length) {
|
||||
const line2 = Tokenizer.readLine(tokenizer);
|
||||
if (!line2 || line2.startsWith(delimiter) || line2.startsWith('> <')) {
|
||||
TokenBuilder.add(data, start, end);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
end = tokenizer.tokenEnd;
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
TokenBuilder.add(data, start, end);
|
||||
}
|
||||
} else {
|
||||
sawHeaderToken = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1167,6 +1167,57 @@ namespace Structure {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ForEachAtomicHierarchyElementParams {
|
||||
// Called for 1st element of each chain
|
||||
// Note that chains can be split, meaning each chain would be called multiple times.
|
||||
chain?: (e: StructureElement.Location<Unit.Atomic>) => void,
|
||||
// Called for 1st element of each residue
|
||||
residue?: (e: StructureElement.Location<Unit.Atomic>) => void,
|
||||
// Called for each element
|
||||
atom?: (e: StructureElement.Location<Unit.Atomic>) => void,
|
||||
};
|
||||
|
||||
export function eachAtomicHierarchyElement(structure: Structure, { chain, residue, atom }: ForEachAtomicHierarchyElementParams) {
|
||||
const l = StructureElement.Location.create<Unit.Atomic>(structure);
|
||||
for (const unit of structure.units) {
|
||||
if (unit.kind !== Unit.Kind.Atomic) continue;
|
||||
|
||||
l.unit = unit;
|
||||
|
||||
const { elements } = unit;
|
||||
const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
|
||||
const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
|
||||
|
||||
while (chainsIt.hasNext) {
|
||||
const chainSegment = chainsIt.move();
|
||||
|
||||
if (chain) {
|
||||
l.element = elements[chainSegment.start];
|
||||
chain(l);
|
||||
}
|
||||
|
||||
if (!residue && !atom) continue;
|
||||
|
||||
residuesIt.setSegment(chainSegment);
|
||||
while (residuesIt.hasNext) {
|
||||
const residueSegment = residuesIt.move();
|
||||
|
||||
if (residue) {
|
||||
l.element = elements[residueSegment.start];
|
||||
residue(l);
|
||||
}
|
||||
|
||||
if (!atom) continue;
|
||||
|
||||
for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
|
||||
l.element = elements[j];
|
||||
atom(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
export const DefaultSizeThresholds = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
@@ -12,7 +12,7 @@ import { StatefulPluginComponent } from '../component';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { utf8ByteCount, utf8Write } from '../../mol-io/common/utf8';
|
||||
import { Asset } from '../../mol-util/assets';
|
||||
import { zip } from '../../mol-util/zip/zip';
|
||||
import { Zip } from '../../mol-util/zip/zip';
|
||||
import { readFromFile } from '../../mol-util/data-source';
|
||||
import { objectForEach } from '../../mol-util/object';
|
||||
import { PLUGIN_VERSION } from '../../mol-plugin/version';
|
||||
@@ -217,7 +217,7 @@ class PluginStateSnapshotManager extends StatefulPluginComponent<{
|
||||
zipDataObj['assets.json'] = data;
|
||||
}
|
||||
|
||||
const zipFile = zip(zipDataObj);
|
||||
const zipFile = await this.plugin.runTask(Zip(zipDataObj));
|
||||
return new Blob([zipFile], {type : 'application/zip'});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ export function MoleculeSvg() { return _Molecule; }
|
||||
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 _CubeSend = <svg width='24px' height='24px' viewBox='0 0 24 24' strokeWidth='0.1px'><path d="M16,4L9,8.04V15.96L16,20L23,15.96V8.04M16,6.31L19.8,8.5L16,10.69L12.21,8.5M0,7V9H7V7M11,10.11L15,12.42V17.11L11,14.81M21,10.11V14.81L17,17.11V12.42M2,11V13H7V11M4,15V17H7V15" /></svg>;
|
||||
export function CubeSendSvg() { return _CubeSend; }
|
||||
|
||||
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; }
|
||||
|
||||
|
||||
@@ -10,14 +10,17 @@ import { Loci } from '../../mol-model/loci';
|
||||
import { StructureElement } from '../../mol-model/structure';
|
||||
import { StructureMeasurementCell, StructureMeasurementOptions, StructureMeasurementParams } from '../../mol-plugin-state/manager/structure/measurement';
|
||||
import { StructureSelectionHistoryEntry } from '../../mol-plugin-state/manager/structure/selection';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { AngleData } from '../../mol-repr/shape/loci/angle';
|
||||
import { DihedralData } from '../../mol-repr/shape/loci/dihedral';
|
||||
import { DistanceData } from '../../mol-repr/shape/loci/distance';
|
||||
import { LabelData } from '../../mol-repr/shape/loci/label';
|
||||
import { angleLabel, dihedralLabel, distanceLabel, lociLabel } from '../../mol-theme/label';
|
||||
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, PencilRulerSvg, SetSvg, ArrowUpwardSvg, ArrowDownwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, AddSvg, TuneSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg, MoreHorizSvg } from '../controls/icons';
|
||||
import { AddSvg, ArrowDownwardSvg, ArrowUpwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, Icon, MoreHorizSvg, PencilRulerSvg, SetSvg, TuneSvg, VisibilityOffOutlinedSvg, VisibilityOutlinedSvg } from '../controls/icons';
|
||||
import { ParameterControls } from '../controls/parameters';
|
||||
import { UpdateTransformControl } from '../state/update-transform';
|
||||
import { ToggleSelectionModeButton } from './selection';
|
||||
@@ -216,7 +219,7 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
}
|
||||
|
||||
get selections() {
|
||||
return this.props.cell.obj?.data.sourceData as ReadonlyArray<PluginStateObject.Molecule.Structure.SelectionEntry> | undefined;
|
||||
return this.props.cell.obj?.data.sourceData as Partial<DistanceData & AngleData & DihedralData & LabelData> | undefined;
|
||||
}
|
||||
|
||||
delete = () => {
|
||||
@@ -234,8 +237,8 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
if (!selections) return;
|
||||
|
||||
this.plugin.managers.interactivity.lociHighlights.clearHighlights();
|
||||
for (const d of selections) {
|
||||
this.plugin.managers.interactivity.lociHighlights.highlight({ loci: d.loci }, false);
|
||||
for (const loci of this.lociArray) {
|
||||
this.plugin.managers.interactivity.lociHighlights.highlight({ loci }, false);
|
||||
}
|
||||
this.plugin.managers.interactivity.lociHighlights.highlight({ loci: this.props.cell.obj?.data.repr.getLoci()! }, false);
|
||||
}
|
||||
@@ -250,21 +253,31 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
const selections = this.selections;
|
||||
if (!selections) return;
|
||||
|
||||
const sphere = Loci.getBundleBoundingSphere(toLociBundle(selections));
|
||||
const sphere = Loci.getBundleBoundingSphere({ loci: this.lociArray });
|
||||
if (sphere) {
|
||||
this.plugin.managers.camera.focusSphere(sphere);
|
||||
}
|
||||
}
|
||||
|
||||
private get lociArray(): FiniteArray<Loci> {
|
||||
const selections = this.selections;
|
||||
if (!selections) return [];
|
||||
if (selections.infos) return [selections.infos[0].loci];
|
||||
if (selections.pairs) return selections.pairs[0].loci;
|
||||
if (selections.triples) return selections.triples[0].loci;
|
||||
if (selections.quads) return selections.quads[0].loci;
|
||||
return [];
|
||||
}
|
||||
|
||||
get label() {
|
||||
const selections = this.selections;
|
||||
switch (selections?.length) {
|
||||
case 1: return lociLabel(selections[0].loci, { condensed: true });
|
||||
case 2: return distanceLabel(toLociBundle(selections), { condensed: true, unitLabel: this.plugin.managers.structure.measurement.state.options.distanceUnitLabel });
|
||||
case 3: return angleLabel(toLociBundle(selections), { condensed: true });
|
||||
case 4: return dihedralLabel(toLociBundle(selections), { condensed: true });
|
||||
default: return '';
|
||||
}
|
||||
|
||||
if (!selections) return '<empty>';
|
||||
if (selections.infos) return lociLabel(selections.infos[0].loci, { condensed: true });
|
||||
if (selections.pairs) return distanceLabel(selections.pairs[0], { condensed: true, unitLabel: this.plugin.managers.structure.measurement.state.options.distanceUnitLabel });
|
||||
if (selections.triples) return angleLabel(selections.triples[0], { condensed: true });
|
||||
if (selections.quads) return dihedralLabel(selections.quads[0], { condensed: true });
|
||||
return '<empty>';
|
||||
}
|
||||
|
||||
get actions(): ActionMenu.Items {
|
||||
@@ -302,8 +315,4 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
</>}
|
||||
</>;
|
||||
}
|
||||
}
|
||||
|
||||
function toLociBundle(data: FiniteArray<{ loci: Loci }, any>): { loci: FiniteArray<Loci, any> } {
|
||||
return { loci: (data.map(d => d.loci) as unknown as FiniteArray<Loci, any>) };
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import { OperatorHklColorThemeProvider } from './color/operator-hkl';
|
||||
import { PartialChargeColorThemeProvider } from './color/partial-charge';
|
||||
import { AtomIdColorThemeProvider } from './color/atom-id';
|
||||
import { EntityIdColorThemeProvider } from './color/entity-id';
|
||||
import { TextureFilter } from '../mol-gl/webgl/texture';
|
||||
|
||||
export type LocationColor = (location: Location, isSecondary: boolean) => Color
|
||||
|
||||
@@ -44,6 +45,9 @@ interface ColorTheme<P extends PD.Params> {
|
||||
readonly granularity: ColorType
|
||||
readonly color: LocationColor
|
||||
readonly props: Readonly<PD.Values<P>>
|
||||
// if palette is defined, 24bit RGB color value normalized to interval [0, 1]
|
||||
// is used as index to the colors
|
||||
readonly palette?: Readonly<ColorTheme.Palette>
|
||||
readonly contextHash?: number
|
||||
readonly description?: string
|
||||
readonly legend?: Readonly<ScaleLegend | TableLegend>
|
||||
@@ -58,6 +62,13 @@ namespace ColorTheme {
|
||||
Misc = 'Miscellaneous',
|
||||
}
|
||||
|
||||
export interface Palette {
|
||||
filter?: TextureFilter,
|
||||
colors: Color[]
|
||||
}
|
||||
|
||||
export const PaletteScale = (1 << 24) - 1;
|
||||
|
||||
export type Props = { [k: string]: any }
|
||||
export type Factory<P extends PD.Params> = (ctx: ThemeDataContext, props: PD.Values<P>) => ColorTheme<P>
|
||||
export const EmptyFactory = () => Empty;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -10,16 +10,16 @@ import { SyncRuntimeContext } from '../../mol-task/execution/synchronous';
|
||||
describe('zip', () => {
|
||||
it('roundtrip deflate/inflate', async () => {
|
||||
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7]);
|
||||
const deflated = deflate(data);
|
||||
const deflated = await deflate(SyncRuntimeContext, data);
|
||||
const inflated = await inflate(SyncRuntimeContext, deflated);
|
||||
expect(inflated).toEqual(data);
|
||||
});
|
||||
|
||||
it('roundtrip zip', async () => {
|
||||
it('roundtrip zip/unzip', async () => {
|
||||
const data = {
|
||||
'test.foo': new Uint8Array([1, 2, 3, 4, 5, 6, 7])
|
||||
};
|
||||
const zipped = zip(data);
|
||||
const zipped = await zip(SyncRuntimeContext, data);
|
||||
const unzipped = await unzip(SyncRuntimeContext, zipped);
|
||||
expect(unzipped).toEqual(data);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*
|
||||
@@ -7,65 +7,54 @@
|
||||
* MIT License, Copyright (c) 2018 Photopea
|
||||
*/
|
||||
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { NumberArray } from '../type-helpers';
|
||||
import { _hufTree } from './huffman';
|
||||
import { U, revCodes, makeCodes } from './util';
|
||||
|
||||
export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl: number) {
|
||||
const opts = [
|
||||
/*
|
||||
ush good_length; /* reduce lazy search above this match length
|
||||
ush max_lazy; /* do not perform lazy search above this match length
|
||||
ush nice_length; /* quit search above this match length
|
||||
*/
|
||||
/* good lazy nice chain */
|
||||
/* 0 */ [ 0, 0, 0, 0, 0], /* store only */
|
||||
/* 1 */ [ 4, 4, 8, 4, 0], /* max speed, no lazy matches */
|
||||
/* 2 */ [ 4, 5, 16, 8, 0],
|
||||
/* 3 */ [ 4, 6, 16, 16, 0],
|
||||
|
||||
/* 4 */ [ 4, 10, 16, 32, 0], /* lazy matches */
|
||||
/* 5 */ [ 8, 16, 32, 32, 0],
|
||||
/* 6 */ [ 8, 16, 128, 128, 0],
|
||||
/* 7 */ [ 8, 32, 128, 256, 0],
|
||||
/* 8 */ [32, 128, 258, 1024, 1],
|
||||
/* 9 */ [32, 258, 258, 4096, 1] /* max compression */
|
||||
];
|
||||
|
||||
const opt = opts[lvl];
|
||||
|
||||
let i = 0, pos = opos << 3, cvrd = 0;
|
||||
const dlen = data.length;
|
||||
|
||||
if(lvl === 0) {
|
||||
while(i < dlen) {
|
||||
const len = Math.min(0xffff, dlen - i);
|
||||
_putsE(out, pos, (i + len === dlen ? 1 : 0));
|
||||
pos = _copyExact(data, i, len, out, pos + 8);
|
||||
i += len;
|
||||
}
|
||||
return pos >>> 3;
|
||||
}
|
||||
|
||||
function DeflateContext(data: Uint8Array, out: Uint8Array, opos: number, lvl: number) {
|
||||
const { lits, strt, prev } = U;
|
||||
let li = 0, lc = 0, bs = 0, ebits = 0, c = 0, nc = 0; // last_item, literal_count, block_start
|
||||
if(dlen > 2) {
|
||||
nc = _hash(data, 0);
|
||||
strt[nc] = 0;
|
||||
}
|
||||
return {
|
||||
data,
|
||||
out,
|
||||
opt: Opts[lvl],
|
||||
i: 0,
|
||||
pos: opos << 3,
|
||||
cvrd: 0,
|
||||
dlen: data.length,
|
||||
|
||||
// let nmch = 0
|
||||
// let nmci = 0
|
||||
li: 0,
|
||||
lc: 0,
|
||||
bs: 0,
|
||||
ebits: 0,
|
||||
c: 0,
|
||||
nc: 0,
|
||||
|
||||
for(i = 0; i < dlen; i++) {
|
||||
lits,
|
||||
strt,
|
||||
prev
|
||||
};
|
||||
}
|
||||
type DeflateContext = ReturnType<typeof DeflateContext>
|
||||
|
||||
|
||||
function deflateChunk(ctx: DeflateContext, count: number) {
|
||||
const { data, dlen, out, opt } = ctx;
|
||||
let { i, pos, cvrd, li, lc, bs, ebits, c, nc } = ctx;
|
||||
const { lits, strt, prev } = U;
|
||||
|
||||
const end = Math.min(i + count, dlen);
|
||||
|
||||
for(; i < end; i++) {
|
||||
c = nc;
|
||||
//*
|
||||
|
||||
if(i + 1 < dlen - 2) {
|
||||
nc = _hash(data, i + 1);
|
||||
const ii = ((i + 1) & 0x7fff);
|
||||
prev[ii] = strt[nc];
|
||||
strt[nc] = ii;
|
||||
} // */
|
||||
}
|
||||
|
||||
if(cvrd <= i) {
|
||||
if((li > 14000 || lc > 26697) && (dlen - i) > 100) {
|
||||
if(cvrd < i) {
|
||||
@@ -79,19 +68,12 @@ export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl
|
||||
}
|
||||
|
||||
let mch = 0;
|
||||
// if(nmci==i) mch= nmch; else
|
||||
if(i < dlen - 2) {
|
||||
mch = _bestMatch(data, i, prev, c, Math.min(opt[2], dlen - i), opt[3]);
|
||||
}
|
||||
/*
|
||||
if(mch!=0 && opt[4]==1 && (mch>>>16)<opt[1] && i+1<dlen-2) {
|
||||
nmch = UZIP.F._bestMatch(data, i+1, prev, nc, opt[2], opt[3]); nmci=i+1;
|
||||
//var mch2 = UZIP.F._bestMatch(data, i+2, prev, nnc); //nmci=i+1;
|
||||
if((nmch>>>16)>(mch>>>16)) mch=0;
|
||||
}//*/
|
||||
// const len = mch>>>16, dst = mch & 0xffff; // if(i-dst<0) throw "e";
|
||||
|
||||
if(mch !== 0) {
|
||||
const len = mch >>> 16, dst = mch & 0xffff; // if(i-dst<0) throw "e";
|
||||
const len = mch >>> 16, dst = mch & 0xffff;
|
||||
const lgi = _goodIndex(len, U.of0); U.lhst[257 + lgi]++;
|
||||
const dgi = _goodIndex(dst, U.df0); U.dhst[ dgi]++; ebits += U.exb[lgi] + U.dxb[dgi];
|
||||
lits[li] = (len << 23) | (i - cvrd); lits[li + 1] = (dst << 16) | (lgi << 8) | dgi; li += 2;
|
||||
@@ -102,6 +84,69 @@ export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl
|
||||
lc++;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.i = i;
|
||||
ctx.pos = pos;
|
||||
ctx.cvrd = cvrd;
|
||||
ctx.li = li;
|
||||
ctx.lc = lc;
|
||||
ctx.bs = bs;
|
||||
ctx.ebits = ebits;
|
||||
ctx.c = c;
|
||||
ctx.nc = nc;
|
||||
}
|
||||
|
||||
/**
|
||||
* - good_length: reduce lazy search above this match length;
|
||||
* - max_lazy: do not perform lazy search above this match length;
|
||||
* - nice_length: quit search above this match length;
|
||||
*/
|
||||
const Opts = [
|
||||
/* good lazy nice chain */
|
||||
/* 0 */ [ 0, 0, 0, 0, 0], /* store only */
|
||||
/* 1 */ [ 4, 4, 8, 4, 0], /* max speed, no lazy matches */
|
||||
/* 2 */ [ 4, 5, 16, 8, 0],
|
||||
/* 3 */ [ 4, 6, 16, 16, 0],
|
||||
|
||||
/* 4 */ [ 4, 10, 16, 32, 0], /* lazy matches */
|
||||
/* 5 */ [ 8, 16, 32, 32, 0],
|
||||
/* 6 */ [ 8, 16, 128, 128, 0],
|
||||
/* 7 */ [ 8, 32, 128, 256, 0],
|
||||
/* 8 */ [32, 128, 258, 1024, 1],
|
||||
/* 9 */ [32, 258, 258, 4096, 1] /* max compression */
|
||||
] as const;
|
||||
|
||||
export async function _deflateRaw(runtime: RuntimeContext, data: Uint8Array, out: Uint8Array, opos: number, lvl: number) {
|
||||
const ctx = DeflateContext(data, out, opos, lvl);
|
||||
const { dlen } = ctx;
|
||||
|
||||
if(lvl === 0) {
|
||||
let { i, pos } = ctx;
|
||||
|
||||
while(i < dlen) {
|
||||
const len = Math.min(0xffff, dlen - i);
|
||||
_putsE(out, pos, (i + len === dlen ? 1 : 0));
|
||||
pos = _copyExact(data, i, len, out, pos + 8);
|
||||
i += len;
|
||||
}
|
||||
return pos >>> 3;
|
||||
}
|
||||
|
||||
if(dlen > 2) {
|
||||
ctx.nc = _hash(data, 0);
|
||||
ctx.strt[ctx.nc] = 0;
|
||||
}
|
||||
|
||||
while (ctx.i < dlen) {
|
||||
if (runtime.shouldUpdate) {
|
||||
await runtime.update({ message: 'Deflating...', current: ctx.i, max: dlen });
|
||||
}
|
||||
deflateChunk(ctx, 1024 * 1024);
|
||||
}
|
||||
|
||||
let { li, cvrd, pos } = ctx;
|
||||
const { i, lits, bs, ebits } = ctx;
|
||||
|
||||
if(bs !== i || data.length === 0) {
|
||||
if(cvrd < i) {
|
||||
lits[li] = i - cvrd;
|
||||
@@ -109,10 +154,6 @@ export function _deflateRaw(data: Uint8Array, out: Uint8Array, opos: number, lvl
|
||||
cvrd = i;
|
||||
}
|
||||
pos = _writeBlock(1, lits, li, ebits, data, bs, i - bs, out, pos);
|
||||
li = 0;
|
||||
lc = 0;
|
||||
li = lc = ebits = 0;
|
||||
bs = i;
|
||||
}
|
||||
while((pos & 7) !== 0) pos++;
|
||||
return pos >>> 3;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { writeUint, writeUshort, sizeUTF8, writeUTF8, readUshort, readUint, read
|
||||
import { crc, adler } from './checksum';
|
||||
import { _inflate } from './inflate';
|
||||
import { _deflateRaw } from './deflate';
|
||||
import { RuntimeContext } from '../../mol-task';
|
||||
import { RuntimeContext, Task } from '../../mol-task';
|
||||
|
||||
export async function unzip(runtime: RuntimeContext, buf: ArrayBuffer, onlyNames = false) {
|
||||
const out: { [k: string]: Uint8Array | { size: number, csize: number } } = Object.create(null);
|
||||
@@ -174,12 +174,12 @@ export async function ungzip(runtime: RuntimeContext, file: Uint8Array, buf?: Ui
|
||||
return inflated;
|
||||
}
|
||||
|
||||
export function deflate(data: Uint8Array, opts?: { level: number }/* , buf, off*/) {
|
||||
export async function deflate(runtime: RuntimeContext, data: Uint8Array, opts?: { level: number }/* , buf, off*/) {
|
||||
if(opts === undefined) opts = { level: 6 };
|
||||
let off = 0;
|
||||
const buf = new Uint8Array(50 + Math.floor(data.length * 1.1));
|
||||
buf[off] = 120; buf[off + 1] = 156; off += 2;
|
||||
off = _deflateRaw(data, buf, off, opts.level);
|
||||
off = await _deflateRaw(runtime, data, buf, off, opts.level);
|
||||
const crcValue = adler(data, 0, data.length);
|
||||
buf[off + 0] = ((crcValue >>> 24) & 255);
|
||||
buf[off + 1] = ((crcValue >>> 16) & 255);
|
||||
@@ -188,14 +188,18 @@ export function deflate(data: Uint8Array, opts?: { level: number }/* , buf, off*
|
||||
return new Uint8Array(buf.buffer, 0, off + 4);
|
||||
}
|
||||
|
||||
function deflateRaw(data: Uint8Array, opts?: { level: number }) {
|
||||
async function deflateRaw(runtime: RuntimeContext, data: Uint8Array, opts?: { level: number }) {
|
||||
if(opts === undefined) opts = { level: 6 };
|
||||
const buf = new Uint8Array(50 + Math.floor(data.length * 1.1));
|
||||
const off = _deflateRaw(data, buf, 0, opts.level);
|
||||
const off = await _deflateRaw(runtime, data, buf, 0, opts.level);
|
||||
return new Uint8Array(buf.buffer, 0, off);
|
||||
}
|
||||
|
||||
export function zip(obj: { [k: string]: Uint8Array }, noCmpr = false) {
|
||||
export function Zip(obj: { [k: string]: Uint8Array }, noCmpr = false) {
|
||||
return Task.create('Zip', ctx => zip(ctx, obj, noCmpr));
|
||||
}
|
||||
|
||||
export async function zip(runtime: RuntimeContext, obj: { [k: string]: Uint8Array }, noCmpr = false) {
|
||||
let tot = 0;
|
||||
const zpd: { [k: string]: { cpr: boolean, usize: number, crc: number, file: Uint8Array } } = {};
|
||||
for(const p in obj) {
|
||||
@@ -205,7 +209,7 @@ export function zip(obj: { [k: string]: Uint8Array }, noCmpr = false) {
|
||||
cpr,
|
||||
usize: buf.length,
|
||||
crc: crcValue,
|
||||
file: (cpr ? deflateRaw(buf) : buf)
|
||||
file: (cpr ? await deflateRaw(runtime, buf) : buf)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user