mirror of
https://github.com/molstar/molstar.git
synced 2026-06-05 22:31:26 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26cc7e94c2 | ||
|
|
c6fe6ddcba | ||
|
|
154984e74d | ||
|
|
72fcaf8321 | ||
|
|
0c14ca5888 | ||
|
|
a85ede5058 | ||
|
|
db49a16184 | ||
|
|
0704db2343 | ||
|
|
425dca4665 | ||
|
|
8d65ccabd2 | ||
|
|
cbd417ca13 | ||
|
|
1578211157 | ||
|
|
15932dc5df | ||
|
|
7db2205956 | ||
|
|
d87f0d236a | ||
|
|
16daca6008 | ||
|
|
a0d919c8db | ||
|
|
ffee2bf1c4 | ||
|
|
de77f6ac59 | ||
|
|
c8c2ebcd65 | ||
|
|
42796b984f | ||
|
|
746557bf52 | ||
|
|
a5020a9e96 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,6 +6,23 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v2.3.9] - 2021-11-20
|
||||
|
||||
- Workaround: switch off VAO support for now
|
||||
|
||||
## [v2.3.8] - 2021-11-20
|
||||
|
||||
- Fix double canvas context creation (in plugin context)
|
||||
- Fix unused vertex attribute handling (track which are used, disable the rest)
|
||||
- Workaround for VAO issue in Chrome 96 (can cause WebGL to crash on geometry updates)
|
||||
|
||||
## [v2.3.7] - 2021-11-15
|
||||
|
||||
- Added ``ViewerOptions.collapseRightPanel``
|
||||
- Added ``Viewer.loadTrajectory`` to support loading "composed" trajectories (e.g. from gro + xtc)
|
||||
- Fix: handle parent in Structure.remapModel
|
||||
- Add ``rounded`` and ``square`` helix profile options to Cartoon representation (in addition to the default ``elliptical``)
|
||||
|
||||
## [v2.3.6] - 2021-11-8
|
||||
|
||||
- Add additional measurement controls: orientation (box, axes, ellipsoid) & plane (best fit)
|
||||
|
||||
2843
package-lock.json
generated
2843
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.3.6",
|
||||
"version": "2.3.9",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -97,6 +97,7 @@
|
||||
"@graphql-codegen/typescript-operations": "^2.1.6",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/gl": "^4.1.0",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.32.0",
|
||||
"@typescript-eslint/parser": "^4.32.0",
|
||||
"benchmark": "^2.1.4",
|
||||
@@ -112,25 +113,23 @@
|
||||
"http-server": "^13.0.2",
|
||||
"jest": "^27.2.4",
|
||||
"mini-css-extract-plugin": "^2.3.0",
|
||||
"node-sass": "^6.0.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass-loader": "^12.1.0",
|
||||
"sass": "^1.43.4",
|
||||
"sass-loader": "^12.3.0",
|
||||
"simple-git": "^2.46.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.0",
|
||||
"ts-jest": "^27.0.5",
|
||||
"typescript": "^4.4.3",
|
||||
"webpack": "^5.56.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-version-file-plugin": "^0.4.0"
|
||||
"typescript": "^4.5.2",
|
||||
"webpack": "^5.64.1",
|
||||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.10",
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@types/compression": "1.7.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/node": "^16.10.2",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/react": "^17.0.27",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
emdbProvider: 'rcsb',
|
||||
});
|
||||
viewer.loadPdb('7bv2');
|
||||
viewer.loadEmdb('EMD-30210', { detail: 6 });
|
||||
viewer.loadEmdb('EMD-30210', { detail: 6 });
|
||||
|
||||
// viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
|
||||
</script>
|
||||
|
||||
@@ -9,25 +9,28 @@ import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
|
||||
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 { Mp4Export } from '../../extensions/mp4-export';
|
||||
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
|
||||
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
|
||||
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
|
||||
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
|
||||
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
|
||||
import { StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
|
||||
import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
|
||||
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
|
||||
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
|
||||
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
|
||||
import { PluginStateObject } from '../../mol-plugin-state/objects';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
|
||||
import { createPlugin } from '../../mol-plugin-ui';
|
||||
import { PluginUIContext } from '../../mol-plugin-ui/context';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
import { PluginState } from '../../mol-plugin/state';
|
||||
import { StateObjectSelector } from '../../mol-state';
|
||||
@@ -71,6 +74,7 @@ const DefaultViewerOptions = {
|
||||
layoutShowLog: true,
|
||||
layoutShowLeftPanel: true,
|
||||
collapseLeftPanel: false,
|
||||
collapseRightPanel: false,
|
||||
disableAntialiasing: PluginConfig.General.DisableAntialiasing.defaultValue,
|
||||
pixelScale: PluginConfig.General.PixelScale.defaultValue,
|
||||
pickScale: PluginConfig.General.PickScale.defaultValue,
|
||||
@@ -114,7 +118,7 @@ export class Viewer {
|
||||
regionState: {
|
||||
bottom: 'full',
|
||||
left: o.collapseLeftPanel ? 'collapsed' : 'full',
|
||||
right: 'full',
|
||||
right: o.collapseRightPanel ? 'hidden' : 'full',
|
||||
top: 'full',
|
||||
}
|
||||
},
|
||||
@@ -328,6 +332,56 @@ export class Viewer {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @example
|
||||
* viewer.loadTrajectory({
|
||||
* model: { kind: 'model-url', url: 'villin.gro', format: 'gro' },
|
||||
* coordinates: { kind: 'coordinates-url', url: 'villin.xtc', format: 'xtc', isBinary: true },
|
||||
* preset: 'all-models' // or 'default'
|
||||
* });
|
||||
*/
|
||||
async loadTrajectory(params: LoadTrajectoryParams) {
|
||||
const plugin = this.plugin;
|
||||
|
||||
let model: StateObjectSelector, coords: StateObjectSelector;
|
||||
|
||||
if (params.model.kind === 'model-data' || params.model.kind === 'model-url') {
|
||||
const data = params.model.kind === 'model-data'
|
||||
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
|
||||
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
|
||||
|
||||
const trajectory = await plugin.builders.structure.parseTrajectory(data, params.model.format ?? 'mmcif');
|
||||
model = await plugin.builders.structure.createModel(trajectory);
|
||||
} else {
|
||||
const data = params.model.kind === 'topology-data'
|
||||
? await plugin.builders.data.rawData({ data: params.model.data, label: params.modelLabel })
|
||||
: await plugin.builders.data.download({ url: params.model.url, isBinary: params.model.isBinary, label: params.modelLabel });
|
||||
|
||||
const provider = plugin.dataFormats.get(params.model.format);
|
||||
model = await provider!.parse(plugin, data);
|
||||
}
|
||||
|
||||
{
|
||||
const data = params.coordinates.kind === 'coordinates-data'
|
||||
? await plugin.builders.data.rawData({ data: params.coordinates.data, label: params.coordinatesLabel })
|
||||
: await plugin.builders.data.download({ url: params.coordinates.url, isBinary: params.coordinates.isBinary, label: params.coordinatesLabel });
|
||||
|
||||
const provider = plugin.dataFormats.get(params.coordinates.format);
|
||||
coords = await provider!.parse(plugin, data);
|
||||
}
|
||||
|
||||
const trajectory = await plugin.build().toRoot()
|
||||
.apply(TrajectoryFromModelAndCoordinates, {
|
||||
modelRef: model.ref,
|
||||
coordinatesRef: coords.ref
|
||||
}, { dependsOn: [model.ref, coords.ref] })
|
||||
.commit();
|
||||
|
||||
const preset = await plugin.builders.structure.hierarchy.applyPreset(trajectory, params.preset ?? 'default');
|
||||
|
||||
return { model, coords, preset };
|
||||
}
|
||||
|
||||
handleResize() {
|
||||
this.plugin.layout.events.updated.next(void 0);
|
||||
}
|
||||
@@ -343,4 +397,16 @@ export interface VolumeIsovalueInfo {
|
||||
color: Color,
|
||||
alpha?: number,
|
||||
volumeIndex?: number
|
||||
}
|
||||
|
||||
export interface LoadTrajectoryParams {
|
||||
model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
|
||||
| { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
|
||||
| { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
|
||||
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
|
||||
modelLabel?: string,
|
||||
coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
|
||||
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
|
||||
coordinatesLabel?: string,
|
||||
preset?: keyof PresetTrajectoryHierarchy
|
||||
}
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -40,7 +40,7 @@ const v3set = Vec3.set;
|
||||
const caAdd3 = ChunkedArray.add3;
|
||||
const caAdd = ChunkedArray.add;
|
||||
|
||||
function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, width: number, leftHeight: number, rightHeight: number) {
|
||||
function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, width: number, leftHeight: number, rightHeight: number, flip: boolean) {
|
||||
const { vertices, normals, indices } = state;
|
||||
const vertexCount = vertices.elementCount;
|
||||
|
||||
@@ -74,11 +74,19 @@ function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLi
|
||||
v3copy(verticalVector, verticalLeftVector);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
if (flip) {
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
caAdd3(normals, -normalVector[0], -normalVector[1], -normalVector[2]);
|
||||
}
|
||||
caAdd3(indices, vertexCount, vertexCount + 1, vertexCount + 2);
|
||||
caAdd3(indices, vertexCount + 2, vertexCount + 3, vertexCount);
|
||||
} else {
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
}
|
||||
caAdd3(indices, vertexCount + 2, vertexCount + 1, vertexCount);
|
||||
caAdd3(indices, vertexCount, vertexCount + 3, vertexCount + 2);
|
||||
}
|
||||
caAdd3(indices, vertexCount + 2, vertexCount + 1, vertexCount);
|
||||
caAdd3(indices, vertexCount, vertexCount + 3, vertexCount + 2);
|
||||
}
|
||||
|
||||
/** set arrowHeight = 0 for no arrow */
|
||||
@@ -193,19 +201,18 @@ export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<numb
|
||||
const width = widthValues[0];
|
||||
const height = heightValues[0];
|
||||
const h = arrowHeight === 0 ? height : arrowHeight;
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, h, h);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, h, h, false);
|
||||
} else if (arrowHeight > 0) {
|
||||
const width = widthValues[0];
|
||||
const height = heightValues[0];
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, arrowHeight, -height);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, -arrowHeight, height);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, arrowHeight, -height, false);
|
||||
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, -arrowHeight, height, false);
|
||||
}
|
||||
|
||||
if (endCap && arrowHeight === 0) {
|
||||
const width = widthValues[linearSegments];
|
||||
const height = heightValues[linearSegments];
|
||||
// use negative height to flip the direction the cap's triangles are facing
|
||||
addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, -height, -height);
|
||||
addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, height, height, true);
|
||||
}
|
||||
|
||||
const addedVertexCount = (linearSegments + 1) * 8 +
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -30,9 +30,10 @@ function add3AndScale2(out: Vec3, a: Vec3, b: Vec3, c: Vec3, sa: number, sb: num
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const v3fromArray = Vec3.fromArray;
|
||||
const v3normalize = Vec3.normalize;
|
||||
const v3negate = Vec3.negate;
|
||||
const v3copy = Vec3.copy;
|
||||
const v3scaleAndAdd = Vec3.scaleAndAdd;
|
||||
const v3cross = Vec3.cross;
|
||||
const v3dot = Vec3.dot;
|
||||
const v3unitX = Vec3.unitX;
|
||||
const caAdd3 = ChunkedArray.add3;
|
||||
|
||||
const CosSinCache = new Map<number, { cos: number[], sin: number[] }>();
|
||||
@@ -50,13 +51,16 @@ function getCosSin(radialSegments: number) {
|
||||
return CosSinCache.get(radialSegments)!;
|
||||
}
|
||||
|
||||
export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, startCap: boolean, endCap: boolean) {
|
||||
export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, startCap: boolean, endCap: boolean, crossSection: 'elliptical' | 'rounded') {
|
||||
const { currentGroup, vertices, normals, indices, groups } = state;
|
||||
|
||||
let vertexCount = vertices.elementCount;
|
||||
|
||||
const { cos, sin } = getCosSin(radialSegments);
|
||||
|
||||
const q1 = radialSegments / 4;
|
||||
const q3 = q1 * 3;
|
||||
|
||||
for (let i = 0; i <= linearSegments; ++i) {
|
||||
const i3 = i * 3;
|
||||
v3fromArray(u, normalVectors, i3);
|
||||
@@ -65,14 +69,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
|
||||
const width = widthValues[i];
|
||||
const height = heightValues[i];
|
||||
const rounded = crossSection === 'rounded' && height > width;
|
||||
|
||||
for (let j = 0; j < radialSegments; ++j) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[j], width * sin[j]);
|
||||
if (radialSegments === 2) {
|
||||
v3copy(normalVector, v);
|
||||
v3normalize(normalVector, normalVector);
|
||||
if (j !== 0 || i % 2 === 0) v3negate(normalVector, normalVector);
|
||||
if (rounded) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[j], width * sin[j]);
|
||||
const h = v3dot(v, v3unitX) < 0
|
||||
? (j < q1 || j >= q3) ? height - width : -height + width
|
||||
: (j >= q1 && j < q3) ? -height + width : height - width;
|
||||
v3scaleAndAdd(surfacePoint, surfacePoint, u, h);
|
||||
add2AndScale2(normalVector, u, v, cos[j], sin[j]);
|
||||
} else {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[j], width * sin[j]);
|
||||
add2AndScale2(normalVector, u, v, width * cos[j], height * sin[j]);
|
||||
}
|
||||
v3normalize(normalVector, normalVector);
|
||||
@@ -82,19 +90,37 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
}
|
||||
}
|
||||
|
||||
const radialSegmentsHalf = Math.round(radialSegments / 2);
|
||||
|
||||
for (let i = 0; i < linearSegments; ++i) {
|
||||
for (let j = 0; j < radialSegments; ++j) {
|
||||
// the triangles are arranged such that opposing triangles of the sheet align
|
||||
// which prevents triangle intersection within tight curves
|
||||
for (let j = 0; j < radialSegmentsHalf; ++j) {
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments,
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments,
|
||||
vertexCount + i * radialSegments + j
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
||||
vertexCount + i * radialSegments + j // b
|
||||
);
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments,
|
||||
vertexCount + (i + 1) * radialSegments + j,
|
||||
vertexCount + i * radialSegments + j
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
||||
vertexCount + (i + 1) * radialSegments + j, // d
|
||||
vertexCount + i * radialSegments + j // b
|
||||
);
|
||||
}
|
||||
for (let j = radialSegmentsHalf; j < radialSegments; ++j) {
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
||||
vertexCount + (i + 1) * radialSegments + j, // d
|
||||
vertexCount + i * radialSegments + j // b
|
||||
);
|
||||
caAdd3(
|
||||
indices,
|
||||
vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
||||
vertexCount + (i + 1) * radialSegments + j, // d
|
||||
vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -111,11 +137,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
|
||||
const width = widthValues[0];
|
||||
const height = heightValues[0];
|
||||
let height = heightValues[0];
|
||||
const rounded = crossSection === 'rounded' && height > width;
|
||||
if (rounded) height -= width;
|
||||
|
||||
vertexCount = vertices.elementCount;
|
||||
for (let i = 0; i < radialSegments; ++i) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
if (rounded) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]);
|
||||
v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height);
|
||||
} else {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
}
|
||||
|
||||
caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]);
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
@@ -141,11 +174,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
|
||||
const width = widthValues[linearSegments];
|
||||
const height = heightValues[linearSegments];
|
||||
let height = heightValues[linearSegments];
|
||||
const rounded = crossSection === 'rounded' && height > width;
|
||||
if (rounded) height -= width;
|
||||
|
||||
vertexCount = vertices.elementCount;
|
||||
for (let i = 0; i < radialSegments; ++i) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
if (rounded) {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]);
|
||||
v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height);
|
||||
} else {
|
||||
add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
||||
}
|
||||
|
||||
caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]);
|
||||
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
||||
|
||||
@@ -245,7 +245,7 @@ export namespace Text {
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
uSizeFactor: ValueCell.create(props.sizeFactor),
|
||||
|
||||
uBorderWidth: ValueCell.create(clamp(props.borderWidth / 2, 0, 0.5)),
|
||||
uBorderWidth: ValueCell.create(clamp(props.borderWidth, 0, 0.5)),
|
||||
uBorderColor: ValueCell.create(Color.toArrayNormalized(props.borderColor, Vec3.zero(), 0)),
|
||||
uOffsetX: ValueCell.create(props.offsetX),
|
||||
uOffsetY: ValueCell.create(props.offsetY),
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('renderer', () => {
|
||||
scene.commit();
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(7);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(8);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 8 : 0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(8);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(16);
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ export function printImageData(imageData: ImageData, options: Partial<PrintImage
|
||||
}
|
||||
|
||||
canvas.toBlob(imgBlob => {
|
||||
const objectURL = URL.createObjectURL(imgBlob);
|
||||
const objectURL = URL.createObjectURL(imgBlob!);
|
||||
const existingImg = document.getElementById(o.id) as HTMLImageElement;
|
||||
const img = existingImg || document.createElement('img');
|
||||
img.id = o.id;
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,6 +11,7 @@ import { idFactory } from '../../mol-util/id-factory';
|
||||
import { ValueOf } from '../../mol-util/type-helpers';
|
||||
import { GLRenderingContext } from './compat';
|
||||
import { WebGLExtensions } from './extensions';
|
||||
import { WebGLState } from './state';
|
||||
|
||||
const getNextBufferId = idFactory();
|
||||
|
||||
@@ -192,7 +193,7 @@ export interface AttributeBuffer extends Buffer {
|
||||
bind: (location: number) => void
|
||||
}
|
||||
|
||||
export function createAttributeBuffer<T extends ArrayType, S extends AttributeItemSize>(gl: GLRenderingContext, extensions: WebGLExtensions, array: T, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer {
|
||||
export function createAttributeBuffer<T extends ArrayType, S extends AttributeItemSize>(gl: GLRenderingContext, state: WebGLState, extensions: WebGLExtensions, array: T, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer {
|
||||
const { instancedArrays } = extensions;
|
||||
|
||||
const buffer = createBuffer(gl, array, usageHint, 'attribute');
|
||||
@@ -204,12 +205,12 @@ export function createAttributeBuffer<T extends ArrayType, S extends AttributeIt
|
||||
gl.bindBuffer(_bufferType, buffer.getBuffer());
|
||||
if (itemSize === 16) {
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
gl.enableVertexAttribArray(location + i);
|
||||
state.enableVertexAttrib(location + i);
|
||||
gl.vertexAttribPointer(location + i, 4, _dataType, false, 4 * 4 * _bpe, i * 4 * _bpe);
|
||||
instancedArrays.vertexAttribDivisor(location + i, divisor);
|
||||
}
|
||||
} else {
|
||||
gl.enableVertexAttribArray(location);
|
||||
state.enableVertexAttrib(location);
|
||||
gl.vertexAttribPointer(location, itemSize, _dataType, false, 0, 0);
|
||||
instancedArrays.vertexAttribDivisor(location, divisor);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat } from './compat';
|
||||
import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear, COMPAT_blend_minmax, getBlendMinMax, getFragDepth, COMPAT_frag_depth, COMPAT_color_buffer_float, getColorBufferFloat, COMPAT_draw_buffers, getDrawBuffers, getShaderTextureLod, COMPAT_shader_texture_lod, getDepthTexture, COMPAT_depth_texture, COMPAT_sRGB, getSRGB, getTextureHalfFloat, getTextureHalfFloatLinear, COMPAT_texture_half_float, COMPAT_texture_half_float_linear, COMPAT_color_buffer_half_float, getColorBufferHalfFloat } from './compat';
|
||||
import { isDebugMode } from '../../mol-util/debug';
|
||||
|
||||
export type WebGLExtensions = {
|
||||
@@ -73,7 +73,11 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
|
||||
// - can't be a required extension because it is not supported by `headless-gl`
|
||||
console.log('Could not find support for "blend_minmax"');
|
||||
}
|
||||
const vertexArrayObject = getVertexArrayObject(gl);
|
||||
// TODO: revisit
|
||||
// switch off VAO support for now
|
||||
// - https://bugs.chromium.org/p/angleproject/issues/detail?id=6599
|
||||
// - https://bugs.chromium.org/p/chromium/issues/detail?id=1272238
|
||||
const vertexArrayObject = null; // getVertexArrayObject(gl);
|
||||
if (isDebugMode && vertexArrayObject === null) {
|
||||
console.log('Could not find support for "vertex_array_object"');
|
||||
}
|
||||
|
||||
@@ -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 Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -39,12 +39,12 @@ function getLocations(gl: GLRenderingContext, program: WebGLProgram, schema: Ren
|
||||
if (spec.type === 'attribute') {
|
||||
const loc = gl.getAttribLocation(program, k);
|
||||
// unused attributes will result in a `-1` location which is usually fine
|
||||
// if (loc === -1) console.info(`Could not get attribute location for '${k}'`)
|
||||
// if (loc === -1) console.info(`Could not get attribute location for '${k}'`);
|
||||
locations[k] = loc;
|
||||
} else if (spec.type === 'uniform' || spec.type === 'texture') {
|
||||
const loc = gl.getUniformLocation(program, k);
|
||||
// unused uniforms will result in a `null` location which is usually fine
|
||||
// if (loc === null) console.info(`Could not get uniform location for '${k}'`)
|
||||
// if (loc === null) console.info(`Could not get uniform location for '${k}'`);
|
||||
locations[k] = loc as number;
|
||||
}
|
||||
});
|
||||
@@ -192,11 +192,13 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi
|
||||
}
|
||||
},
|
||||
bindAttributes: (attributeBuffers: AttributeBuffers) => {
|
||||
state.clearVertexAttribsState();
|
||||
for (let i = 0, il = attributeBuffers.length; i < il; ++i) {
|
||||
const [k, buffer] = attributeBuffers[i];
|
||||
const l = locations[k];
|
||||
if (l !== -1) buffer.bind(l);
|
||||
}
|
||||
state.disableUnusedVertexAttribs();
|
||||
},
|
||||
bindTextures: (textures: Textures, startingTargetUnit: number) => {
|
||||
for (let i = 0, il = textures.length; i < il; ++i) {
|
||||
|
||||
@@ -67,15 +67,9 @@ function createProgramVariant(ctx: WebGLContext, variant: string, defineValues:
|
||||
|
||||
//
|
||||
|
||||
type ProgramVariants = { [k: string]: Program }
|
||||
type VertexArrayVariants = { [k: string]: VertexArray | null }
|
||||
type ProgramVariants = Record<string, Program>
|
||||
type VertexArrayVariants = Record<string, VertexArray | null>
|
||||
|
||||
interface ValueChanges {
|
||||
attributes: boolean
|
||||
defines: boolean
|
||||
elements: boolean
|
||||
textures: boolean
|
||||
}
|
||||
function createValueChanges() {
|
||||
return {
|
||||
attributes: false,
|
||||
@@ -84,6 +78,8 @@ function createValueChanges() {
|
||||
textures: false,
|
||||
};
|
||||
}
|
||||
type ValueChanges = ReturnType<typeof createValueChanges>
|
||||
|
||||
function resetValueChanges(valueChanges: ValueChanges) {
|
||||
valueChanges.attributes = false;
|
||||
valueChanges.defines = false;
|
||||
|
||||
@@ -114,7 +114,7 @@ export function createResources(gl: GLRenderingContext, state: WebGLState, stats
|
||||
|
||||
return {
|
||||
attribute: (array: ArrayType, itemSize: AttributeItemSize, divisor: number, usageHint?: UsageHint) => {
|
||||
return wrap('attribute', createAttributeBuffer(gl, extensions, array, itemSize, divisor, usageHint));
|
||||
return wrap('attribute', createAttributeBuffer(gl, state, extensions, array, itemSize, divisor, usageHint));
|
||||
},
|
||||
elements: (array: ElementsType, usageHint?: UsageHint) => {
|
||||
return wrap('elements', createElementsBuffer(gl, array, usageHint));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -59,11 +59,15 @@ export type WebGLState = {
|
||||
/** set the RGB blend equation and alpha blend equation separately, determines how a new pixel is combined with an existing */
|
||||
blendEquationSeparate: (modeRGB: number, modeAlpha: number) => void
|
||||
|
||||
enableVertexAttrib: (index: number) => void
|
||||
clearVertexAttribsState: () => void
|
||||
disableUnusedVertexAttribs: () => void
|
||||
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
export function createState(gl: GLRenderingContext): WebGLState {
|
||||
let enabledCapabilities: { [k: number]: boolean } = {};
|
||||
let enabledCapabilities: Record<number, boolean> = {};
|
||||
|
||||
let currentFrontFace = gl.getParameter(gl.FRONT_FACE);
|
||||
let currentCullFace = gl.getParameter(gl.CULL_FACE_MODE);
|
||||
@@ -79,6 +83,16 @@ export function createState(gl: GLRenderingContext): WebGLState {
|
||||
let currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB);
|
||||
let currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA);
|
||||
|
||||
let maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
|
||||
const vertexAttribsState: number[] = [];
|
||||
|
||||
const clearVertexAttribsState = () => {
|
||||
for (let i = 0; i < maxVertexAttribs; ++i) {
|
||||
vertexAttribsState[i] = 0;
|
||||
}
|
||||
};
|
||||
clearVertexAttribsState();
|
||||
|
||||
return {
|
||||
currentProgramId: -1,
|
||||
currentMaterialId: -1,
|
||||
@@ -168,6 +182,17 @@ export function createState(gl: GLRenderingContext): WebGLState {
|
||||
}
|
||||
},
|
||||
|
||||
enableVertexAttrib: (index: number) => {
|
||||
gl.enableVertexAttribArray(index);
|
||||
vertexAttribsState[index] = 1;
|
||||
},
|
||||
clearVertexAttribsState,
|
||||
disableUnusedVertexAttribs: () => {
|
||||
for (let i = 0; i < maxVertexAttribs; ++i) {
|
||||
if (vertexAttribsState[i] === 0) gl.disableVertexAttribArray(i);
|
||||
}
|
||||
},
|
||||
|
||||
reset: () => {
|
||||
enabledCapabilities = {};
|
||||
|
||||
@@ -184,6 +209,12 @@ export function createState(gl: GLRenderingContext): WebGLState {
|
||||
|
||||
currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB);
|
||||
currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA);
|
||||
|
||||
maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
|
||||
vertexAttribsState.length = 0;
|
||||
for (let i = 0; i < maxVertexAttribs; ++i) {
|
||||
vertexAttribsState[i] = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -355,8 +355,8 @@ class Structure {
|
||||
return this.models.indexOf(m);
|
||||
}
|
||||
|
||||
remapModel(m: Model) {
|
||||
const { dynamicBonds, interUnitBonds } = this.state;
|
||||
remapModel(m: Model): Structure {
|
||||
const { dynamicBonds, interUnitBonds, parent } = this.state;
|
||||
const units: Unit[] = [];
|
||||
for (const ug of this.unitSymmetryGroups) {
|
||||
const unit = ug.units[0].remapModel(m, dynamicBonds);
|
||||
@@ -367,6 +367,7 @@ class Structure {
|
||||
}
|
||||
}
|
||||
return Structure.create(units, {
|
||||
parent: parent?.remapModel(m),
|
||||
label: this.label,
|
||||
interUnitBonds: dynamicBonds ? undefined : interUnitBonds,
|
||||
dynamicBonds
|
||||
|
||||
@@ -308,8 +308,9 @@ class StructureMeasurementManager extends StatefulPluginComponent<StructureMeasu
|
||||
.apply(StateTransforms.Representation.StructureSelectionsLabel3D, {
|
||||
textColor: Color.fromRgb(255, 255, 255),
|
||||
borderColor: Color.fromRgb(0, 0, 0),
|
||||
borderWidth: 0.5,
|
||||
textSize: 0.33,
|
||||
borderWidth: 0.3,
|
||||
offsetZ: 0.75,
|
||||
customText: `${order++}`
|
||||
}, { tags: MeasurementOrderLabelTag });
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@use "sass:math";
|
||||
|
||||
.msp-control-row {
|
||||
position: relative;
|
||||
height: $row-height;
|
||||
@@ -184,7 +186,7 @@
|
||||
z-index: 100000;
|
||||
background: $default-background;
|
||||
border-top: 1px solid $default-background;
|
||||
padding-bottom: $control-spacing / 2;
|
||||
padding-bottom: math.div($control-spacing, 2);
|
||||
width: 100%;
|
||||
|
||||
// input[type=text] {
|
||||
@@ -195,8 +197,8 @@
|
||||
|
||||
.msp-toggle-color-picker-above {
|
||||
.msp-color-picker {
|
||||
top: -2 * 32px - 16px - $control-spacing / 2;
|
||||
height: 2 * 32px + 16px + $control-spacing / 2;
|
||||
top: -2 * 32px - 16px - math.div($control-spacing, 2);
|
||||
height: 2 * 32px + 16px + math.div($control-spacing, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,10 +210,6 @@
|
||||
}
|
||||
|
||||
.msp-control-offset {
|
||||
// border-left-width: $control-spacing / 2;
|
||||
// border-left-style: solid;
|
||||
// border-left-color: color-increase-contrast($default-background, 10%);
|
||||
// padding-left: 1px;
|
||||
padding-left: $control-spacing;
|
||||
}
|
||||
|
||||
@@ -228,7 +226,7 @@
|
||||
// }
|
||||
|
||||
.msp-control-group-wrapper {
|
||||
//border-left-width: $control-spacing / 2;
|
||||
//border-left-width: math.div($control-spacing, 2);
|
||||
//border-left-style: solid;
|
||||
//border-left-color: color-increase-contrast($default-background, 10%);
|
||||
|
||||
@@ -240,10 +238,10 @@
|
||||
.msp-control-group-header {
|
||||
background: $default-background;
|
||||
> button, div {
|
||||
padding-left: 4px; // $control-spacing / 2 !important;
|
||||
padding-left: 4px; // math.div($control-spacing, 2) !important;
|
||||
text-align: left;
|
||||
height: 24px !important; // 2 * $row-height / 3 !important;
|
||||
line-height: 24px !important; // 2 * $row-height / 3 !important;
|
||||
height: 24px !important;
|
||||
line-height: 24px !important;
|
||||
font-size: 85% !important;
|
||||
background: $default-background !important;
|
||||
color: color-lower-contrast($font-color, 15%);
|
||||
@@ -253,8 +251,8 @@
|
||||
line-height: 24px !important;
|
||||
}
|
||||
> span {
|
||||
padding-left: $control-spacing / 2;
|
||||
line-height: 2 * $row-height / 3;
|
||||
padding-left: math.div($control-spacing, 2);
|
||||
line-height: math.div(2 * $row-height, 3);
|
||||
font-size: 70%;
|
||||
background: $default-background;
|
||||
color: color-lower-contrast($font-color, 15%);
|
||||
@@ -267,7 +265,7 @@
|
||||
|
||||
.msp-control-group-footer {
|
||||
background: color-increase-contrast($default-background, 5%);
|
||||
height: $control-spacing / 2;
|
||||
height: math.div($control-spacing, 2);
|
||||
font-size: 1px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
@@ -339,7 +337,7 @@
|
||||
margin-top: 1px;
|
||||
|
||||
> div {
|
||||
padding: ($control-spacing / 2) $control-spacing;
|
||||
padding: (math.div($control-spacing, 2)) $control-spacing;
|
||||
text-align: left;
|
||||
color: color-lower-contrast($font-color, 15%);
|
||||
}
|
||||
@@ -359,7 +357,7 @@
|
||||
height: $control-spacing * 3;
|
||||
|
||||
> span {
|
||||
padding: $control-spacing / 2;
|
||||
padding: math.div($control-spacing, 2);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
@@ -370,7 +368,7 @@
|
||||
.msp-table-legend {
|
||||
> div {
|
||||
// min-width: 60px;
|
||||
margin-right: $control-spacing / 2;
|
||||
margin-right: math.div($control-spacing, 2);
|
||||
display: inline-flex;
|
||||
|
||||
.msp-table-legend-color {
|
||||
@@ -379,7 +377,7 @@
|
||||
}
|
||||
|
||||
.msp-table-legend-text {
|
||||
margin: 0 ($control-spacing / 2);
|
||||
margin: 0 (math.div($control-spacing, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
.msp-toast-container {
|
||||
position: relative;
|
||||
// bottom: $control-spacing;
|
||||
@@ -75,7 +77,7 @@
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
padding-right: $control-spacing / 2;
|
||||
padding-right: math.div($control-spacing, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,8 +199,7 @@ export class PluginContext {
|
||||
const pickPadding = this.config.get(PluginConfig.General.PickPadding) ?? 1;
|
||||
const enableWboit = this.config.get(PluginConfig.General.EnableWboit) || false;
|
||||
const preferWebGl1 = this.config.get(PluginConfig.General.PreferWebGl1) || false;
|
||||
(this.canvas3dContext as Canvas3DContext) = Canvas3DContext.fromCanvas(canvas, { antialias, preserveDrawingBuffer, pixelScale, pickScale, enableWboit, preferWebGl1 });
|
||||
(this.canvas3dContext as Canvas3DContext) = Canvas3DContext.fromCanvas(canvas, { antialias, preserveDrawingBuffer, pixelScale, pickScale, pickPadding, enableWboit });
|
||||
(this.canvas3dContext as Canvas3DContext) = Canvas3DContext.fromCanvas(canvas, { antialias, preserveDrawingBuffer, pixelScale, pickScale, pickPadding, enableWboit, preferWebGl1 });
|
||||
}
|
||||
(this.canvas3d as Canvas3D) = Canvas3D.create(this.canvas3dContext!);
|
||||
this.canvas3dInit.next(true);
|
||||
|
||||
@@ -27,8 +27,9 @@ import { StructureGroup } from './util/common';
|
||||
export const PolymerTraceMeshParams = {
|
||||
sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
|
||||
aspectRatio: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }),
|
||||
arrowFactor: PD.Numeric(1.5, { min: 0, max: 3, step: 0.1 }),
|
||||
tubularHelices: PD.Boolean(false),
|
||||
arrowFactor: PD.Numeric(1.5, { min: 0, max: 3, step: 0.1 }, { description: 'Size factor for sheet arrows' }),
|
||||
tubularHelices: PD.Boolean(false, { description: 'Draw alpha helices as tubes' }),
|
||||
helixProfile: PD.Select('elliptical', PD.arrayToOptions(['elliptical', 'rounded', 'square'] as const), { description: 'Protein and nucleic helix trace profile' }),
|
||||
detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }, BaseGeometry.CustomQualityParamInfo),
|
||||
linearSegments: PD.Numeric(8, { min: 1, max: 48, step: 1 }, BaseGeometry.CustomQualityParamInfo),
|
||||
radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }, BaseGeometry.CustomQualityParamInfo)
|
||||
@@ -42,7 +43,7 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc
|
||||
const polymerElementCount = unit.polymerElements.length;
|
||||
|
||||
if (!polymerElementCount) return Mesh.createEmpty(mesh);
|
||||
const { sizeFactor, detail, linearSegments, radialSegments, aspectRatio, arrowFactor, tubularHelices } = props;
|
||||
const { sizeFactor, detail, linearSegments, radialSegments, aspectRatio, arrowFactor, tubularHelices, helixProfile } = props;
|
||||
|
||||
const vertexCount = linearSegments * radialSegments * polymerElementCount + (radialSegments + 1) * polymerElementCount * 2;
|
||||
const builderState = MeshBuilder.createState(vertexCount, vertexCount / 10, mesh);
|
||||
@@ -131,9 +132,6 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc
|
||||
h0 = w0 * aspectRatio;
|
||||
h1 = w1 * aspectRatio;
|
||||
h2 = w2 * aspectRatio;
|
||||
[w0, h0] = [h0, w0];
|
||||
[w1, h1] = [h1, w1];
|
||||
[w2, h2] = [h2, w2];
|
||||
} else {
|
||||
h0 = w0;
|
||||
h1 = w1;
|
||||
@@ -142,18 +140,26 @@ function createPolymerTraceMesh(ctx: VisualContext, unit: Unit, structure: Struc
|
||||
|
||||
interpolateSizes(state, w0, w1, w2, h0, h1, h2, shift);
|
||||
|
||||
const [normals, binormals] = isNucleicType && !v.isCoarseBackbone ? [binormalVectors, normalVectors] : [normalVectors, binormalVectors];
|
||||
if (isNucleicType && !v.isCoarseBackbone) {
|
||||
// TODO: find a cleaner way to swap normal and binormal for nucleic types
|
||||
for (let i = 0, il = normals.length; i < il; i++) normals[i] *= -1;
|
||||
}
|
||||
|
||||
if (radialSegments === 2) {
|
||||
if (isNucleicType && !v.isCoarseBackbone) {
|
||||
// TODO find a cleaner way to swap normal and binormal for nucleic types
|
||||
for (let i = 0, il = binormalVectors.length; i < il; i++) binormalVectors[i] *= -1;
|
||||
addRibbon(builderState, curvePoints, binormalVectors, normalVectors, segmentCount, heightValues, widthValues, 0);
|
||||
addRibbon(builderState, curvePoints, normals, binormals, segmentCount, heightValues, widthValues, 0);
|
||||
} else {
|
||||
addRibbon(builderState, curvePoints, normalVectors, binormalVectors, segmentCount, widthValues, heightValues, 0);
|
||||
addRibbon(builderState, curvePoints, normals, binormals, segmentCount, widthValues, heightValues, 0);
|
||||
}
|
||||
} else if (radialSegments === 4) {
|
||||
addSheet(builderState, curvePoints, normalVectors, binormalVectors, segmentCount, widthValues, heightValues, 0, startCap, endCap);
|
||||
addSheet(builderState, curvePoints, normals, binormals, segmentCount, widthValues, heightValues, 0, startCap, endCap);
|
||||
} else if (h1 === w1) {
|
||||
addTube(builderState, curvePoints, normals, binormals, segmentCount, radialSegments, widthValues, heightValues, startCap, endCap, 'elliptical');
|
||||
} else if (helixProfile === 'square') {
|
||||
addSheet(builderState, curvePoints, normals, binormals, segmentCount, widthValues, heightValues, 0, startCap, endCap);
|
||||
} else {
|
||||
addTube(builderState, curvePoints, normalVectors, binormalVectors, segmentCount, radialSegments, widthValues, heightValues, startCap, endCap);
|
||||
addTube(builderState, curvePoints, normals, binormals, segmentCount, radialSegments, widthValues, heightValues, startCap, endCap, helixProfile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +195,8 @@ export function PolymerTraceVisual(materialId: number): UnitsVisual<PolymerTrace
|
||||
newProps.linearSegments !== currentProps.linearSegments ||
|
||||
newProps.radialSegments !== currentProps.radialSegments ||
|
||||
newProps.aspectRatio !== currentProps.aspectRatio ||
|
||||
newProps.arrowFactor !== currentProps.arrowFactor
|
||||
newProps.arrowFactor !== currentProps.arrowFactor ||
|
||||
newProps.helixProfile !== currentProps.helixProfile
|
||||
);
|
||||
|
||||
const secondaryStructureHash = SecondaryStructureProvider.get(newStructureGroup.structure).version;
|
||||
|
||||
@@ -93,7 +93,7 @@ function createPolymerTubeMesh(ctx: VisualContext, unit: Unit, structure: Struct
|
||||
} else if (radialSegments === 4) {
|
||||
addSheet(builderState, curvePoints, normalVectors, binormalVectors, segmentCount, widthValues, heightValues, 0, startCap, endCap);
|
||||
} else {
|
||||
addTube(builderState, curvePoints, normalVectors, binormalVectors, segmentCount, radialSegments, widthValues, heightValues, startCap, endCap);
|
||||
addTube(builderState, curvePoints, normalVectors, binormalVectors, segmentCount, radialSegments, widthValues, heightValues, startCap, endCap, 'elliptical');
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
@@ -61,7 +61,7 @@ export function download(data: Blob | string, downloadName = 'download') {
|
||||
open(data);
|
||||
}
|
||||
} else {
|
||||
const url = URL.createObjectURL(data);
|
||||
const url = URL.createObjectURL(typeof data === 'string' ? new Blob([data]) : data);
|
||||
location.href = url;
|
||||
setTimeout(() => URL.revokeObjectURL(url), 4E4); // 40s
|
||||
}
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
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 VERSION = require('./package.json').version;
|
||||
|
||||
class VersionFilePlugin {
|
||||
apply() {
|
||||
fs.writeFileSync(
|
||||
path.resolve(__dirname, 'lib/mol-plugin/version.js'),
|
||||
`export var PLUGIN_VERSION = '${VERSION}';\nexport var PLUGIN_VERSION_DATE = new Date(typeof __MOLSTAR_DEBUG_TIMESTAMP__ !== 'undefined' ? __MOLSTAR_DEBUG_TIMESTAMP__ : ${new Date().valueOf()});`);
|
||||
}
|
||||
}
|
||||
|
||||
const sharedConfig = {
|
||||
module: {
|
||||
@@ -36,12 +45,7 @@ const sharedConfig = {
|
||||
'__MOLSTAR_DEBUG_TIMESTAMP__': webpack.DefinePlugin.runtimeValue(() => `${new Date().valueOf()}`, true)
|
||||
}),
|
||||
new MiniCssExtractPlugin({ filename: 'molstar.css' }),
|
||||
new VersionFile({
|
||||
extras: { timestamp: `${new Date().valueOf()}` },
|
||||
packageFile: path.resolve(__dirname, 'package.json'),
|
||||
templateString: `export var PLUGIN_VERSION = '<%= package.version %>';\nexport var PLUGIN_VERSION_DATE = new Date(typeof __MOLSTAR_DEBUG_TIMESTAMP__ !== 'undefined' ? __MOLSTAR_DEBUG_TIMESTAMP__ : <%= extras.timestamp %>);`,
|
||||
outputFile: path.resolve(__dirname, 'lib/mol-plugin/version.js')
|
||||
})
|
||||
new VersionFilePlugin(),
|
||||
],
|
||||
resolve: {
|
||||
modules: [
|
||||
|
||||
Reference in New Issue
Block a user