mirror of
https://github.com/molstar/molstar.git
synced 2026-06-06 14:44:22 +08:00
Compare commits
14 Commits
v2.0.0-dev
...
v2.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31ba8212da | ||
|
|
fe27d8e134 | ||
|
|
83dcdfdc4b | ||
|
|
f9aaabc1f7 | ||
|
|
034370b44c | ||
|
|
b87666df3e | ||
|
|
c98c3228fe | ||
|
|
9419980dfc | ||
|
|
42d60420e5 | ||
|
|
5b1df333a7 | ||
|
|
0bb376706d | ||
|
|
eca7da2c72 | ||
|
|
b0bdb3ddb6 | ||
|
|
3180d7c305 |
@@ -27,3 +27,4 @@
|
||||
* DNA (5D3G)
|
||||
* Multiple models with different sets of ligands or missing ligands (1J6T, 1VRC, 2ICY, 1O2F)
|
||||
* Long linear sugar chain (4HG6)
|
||||
* Anisotropic B-factors/Ellipsoids (1EJG)
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.0.0-dev.11",
|
||||
"version": "2.0.0-dev.12",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "2.0.0-dev.11",
|
||||
"version": "2.0.0-dev.12",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -4,24 +4,23 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import { Viewport, ViewportControls } from '../../mol-plugin-ui/viewport';
|
||||
import { BackgroundTaskProgress } from '../../mol-plugin-ui/task';
|
||||
import { LociLabels } from '../../mol-plugin-ui/controls';
|
||||
import { Toasts } from '../../mol-plugin-ui/toast';
|
||||
import { Button } from '../../mol-plugin-ui/controls/common';
|
||||
import { StructureRepresentationPresetProvider, presetStaticComponent } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { StateObjectRef } from '../../mol-state';
|
||||
import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { InteractionsRepresentationProvider } from '../../mol-model-props/computed/representations/interactions';
|
||||
import { InteractionTypeColorThemeProvider } from '../../mol-model-props/computed/themes/interaction-type';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { presetStaticComponent, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
|
||||
import { StructureSelectionQueries, StructureSelectionQuery } from '../../mol-plugin-state/helpers/structure-selection-query';
|
||||
import { StructureRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import { LociLabels } from '../../mol-plugin-ui/controls';
|
||||
import { Button } from '../../mol-plugin-ui/controls/common';
|
||||
import { BackgroundTaskProgress } from '../../mol-plugin-ui/task';
|
||||
import { Toasts } from '../../mol-plugin-ui/toast';
|
||||
import { Viewport, ViewportControls } from '../../mol-plugin-ui/viewport';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { PluginConfig } from '../../mol-plugin/config';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
|
||||
import { StateObjectRef } from '../../mol-state';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
function shinyStyle(plugin: PluginContext) {
|
||||
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { AlphaOrbitalsExample } from '.';
|
||||
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import * as React from 'react';
|
||||
|
||||
export class CustomToastMessage extends PluginUIComponent {
|
||||
render() {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { PluginUIContext } from '../../../mol-plugin-ui/context';
|
||||
import { PluginContextContainer } from '../../../mol-plugin-ui/plugin';
|
||||
|
||||
@@ -190,7 +190,7 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, params));
|
||||
await repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
repr.setState({ pickable: srcParams.pickable });
|
||||
return new PluginStateObject.Volume.Representation3D({ repr, source: a }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
|
||||
return new PluginStateObject.Volume.Representation3D({ repr, sourceData: a.data }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
|
||||
});
|
||||
},
|
||||
update({ a, b, newParams: srcParams }, plugin: PluginContext) {
|
||||
@@ -200,6 +200,7 @@ export const CreateOrbitalRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams.type.params };
|
||||
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, newParams));
|
||||
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
b.data.sourceData = a.data;
|
||||
b.data.repr.setState({ pickable: srcParams.pickable });
|
||||
b.description = VolumeRepresentation3DHelpers.getDescription(props);
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
|
||||
@@ -121,7 +121,7 @@ const MembraneOrientation3D = PluginStateTransform.BuiltIn({
|
||||
await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
|
||||
const repr = MembraneOrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => MembraneOrientationParams);
|
||||
await repr.createOrUpdate(params, a.data).runInContext(ctx);
|
||||
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: 'Membrane Orientation' });
|
||||
return new PluginStateObject.Shape.Representation3D({ repr, sourceData: a.data }, { label: 'Membrane Orientation' });
|
||||
});
|
||||
},
|
||||
update({ a, b, newParams }, plugin: PluginContext) {
|
||||
@@ -129,6 +129,7 @@ const MembraneOrientation3D = PluginStateTransform.BuiltIn({
|
||||
await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
b.data.sourceData = a.data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -414,7 +414,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
|
||||
const structure = new Structure(units);
|
||||
const structure = Structure.create(units);
|
||||
for( let i = 0, il = structure.models.length; i < il; ++i) {
|
||||
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { merge } from 'rxjs';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
|
||||
|
||||
@@ -124,7 +124,7 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
|
||||
const repr = AssemblySymmetryRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AssemblySymmetryParams);
|
||||
await repr.createOrUpdate(params, a.data).runInContext(ctx);
|
||||
const { type, kind, symbol } = assemblySymmetry;
|
||||
return new PluginStateObject.Shape.Representation3D({ repr, source: a }, { label: kind, description: `${type} (${symbol})` });
|
||||
return new PluginStateObject.Shape.Representation3D({ repr, sourceData: a.data }, { label: kind, description: `${type} (${symbol})` });
|
||||
});
|
||||
},
|
||||
update({ a, b, newParams }, plugin: PluginContext) {
|
||||
@@ -138,6 +138,7 @@ const AssemblySymmetry3D = PluginStateTransform.BuiltIn({
|
||||
}
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
b.data.sourceData = a.data;
|
||||
const { type, kind, symbol } = assemblySymmetry;
|
||||
b.label = kind;
|
||||
b.description = `${type} (${symbol})`;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { CollapsableState, CollapsableControls } from '../../../mol-plugin-ui/base';
|
||||
import { ApplyActionControl } from '../../../mol-plugin-ui/state/apply-action';
|
||||
import { InitAssemblySymmetry3D, AssemblySymmetry3D, AssemblySymmetryPreset, tryCreateAssemblySymmetry } from './behavior';
|
||||
|
||||
@@ -177,7 +177,7 @@ export namespace Spheres {
|
||||
|
||||
const counts = { drawCount: spheres.sphereCount * 2 * 3, vertexCount: spheres.sphereCount * 4, groupCount, instanceCount };
|
||||
|
||||
const padding = getMaxSize(size) * props.sizeFactor;
|
||||
const padding = spheres.boundingSphere.radius ? getMaxSize(size) * props.sizeFactor : 0;
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
||||
|
||||
@@ -222,7 +222,9 @@ export namespace Spheres {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {
|
||||
const padding = getMaxSize(values) * values.uSizeFactor.ref.value;
|
||||
const padding = spheres.boundingSphere.radius
|
||||
? getMaxSize(values) * values.uSizeFactor.ref.value
|
||||
: 0;
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding);
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -15,7 +15,7 @@ import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../../../mo
|
||||
import { VisualUpdateState } from '../../../mol-repr/util';
|
||||
import { PickingId } from '../../../mol-geo/geometry/picking';
|
||||
import { EmptyLoci, Loci } from '../../../mol-model/loci';
|
||||
import { Interval, OrderedSet } from '../../../mol-data/int';
|
||||
import { Interval, OrderedSet, SortedArray } from '../../../mol-data/int';
|
||||
import { Interactions } from '../interactions/interactions';
|
||||
import { InteractionsProvider } from '../interactions';
|
||||
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
|
||||
@@ -35,6 +35,8 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
|
||||
|
||||
if (!edgeCount) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { child } = structure;
|
||||
|
||||
const builderProps = {
|
||||
linkCount: edgeCount,
|
||||
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
|
||||
@@ -63,12 +65,28 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
|
||||
const sizeB = theme.size.size(l);
|
||||
return Math.min(sizeA, sizeB) * sizeFactor;
|
||||
},
|
||||
ignore: (edgeIndex: number) => edges[edgeIndex].props.flag === InteractionFlag.Filtered
|
||||
ignore: (edgeIndex: number) => {
|
||||
if (edges[edgeIndex].props.flag === InteractionFlag.Filtered) return true;
|
||||
|
||||
if (child) {
|
||||
const b = edges[edgeIndex];
|
||||
const childUnitA = child.unitMap.get(b.unitA);
|
||||
if (!childUnitA) return true;
|
||||
|
||||
const unitA = structure.unitMap.get(b.unitA);
|
||||
const fA = unitsFeatures.get(b.unitA);
|
||||
// TODO: check all members
|
||||
const eA = unitA.elements[fA.members[fA.offsets[b.indexA]]];
|
||||
if (!SortedArray.has(childUnitA.elements, eA)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
|
||||
return m;
|
||||
@@ -80,6 +98,7 @@ export const InteractionsInterUnitParams = {
|
||||
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
|
||||
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
|
||||
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
|
||||
includeParent: PD.Boolean(false),
|
||||
};
|
||||
export type InteractionsInterUnitParams = typeof InteractionsInterUnitParams
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -7,7 +7,7 @@
|
||||
import { Unit, Structure, StructureElement } from '../../../mol-model/structure';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { Loci, EmptyLoci } from '../../../mol-model/loci';
|
||||
import { Interval, OrderedSet } from '../../../mol-data/int';
|
||||
import { Interval, OrderedSet, SortedArray } from '../../../mol-data/int';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
|
||||
import { PickingId } from '../../../mol-geo/geometry/picking';
|
||||
@@ -25,6 +25,10 @@ import { Sphere3D } from '../../../mol-math/geometry';
|
||||
async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<InteractionsIntraUnitParams>, mesh?: Mesh) {
|
||||
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) return Mesh.createEmpty(mesh);
|
||||
|
||||
const location = StructureElement.Location.create(structure, unit);
|
||||
|
||||
const interactions = InteractionsProvider.get(structure).value!;
|
||||
@@ -51,12 +55,16 @@ async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit:
|
||||
const sizeB = theme.size.size(location);
|
||||
return Math.min(sizeA, sizeB) * sizeFactor;
|
||||
},
|
||||
ignore: (edgeIndex: number) => flag[edgeIndex] === InteractionFlag.Filtered
|
||||
ignore: (edgeIndex: number) => (
|
||||
flag[edgeIndex] === InteractionFlag.Filtered ||
|
||||
// TODO: check all members
|
||||
(!!childUnit && !SortedArray.has(childUnit.elements, unit.elements[members[offsets[a[edgeIndex]]]]))
|
||||
)
|
||||
};
|
||||
|
||||
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * sizeFactor);
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
|
||||
return m;
|
||||
@@ -68,6 +76,7 @@ export const InteractionsIntraUnitParams = {
|
||||
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
|
||||
dashCount: PD.Numeric(6, { min: 2, max: 10, step: 2 }),
|
||||
dashScale: PD.Numeric(0.4, { min: 0, max: 2, step: 0.1 }),
|
||||
includeParent: PD.Boolean(false),
|
||||
};
|
||||
export type InteractionsIntraUnitParams = typeof InteractionsIntraUnitParams
|
||||
|
||||
@@ -147,7 +156,7 @@ function eachInteraction(loci: Loci, structureGroup: StructureGroup, apply: (int
|
||||
const { offset } = contacts;
|
||||
const { offsets: fOffsets, indices: fIndices } = features.elementsIndex;
|
||||
|
||||
// TODO when isMarking, all elements of contact features need to be in the loci
|
||||
// TODO: when isMarking, all elements of contact features need to be in the loci
|
||||
for (const e of loci.elements) {
|
||||
const unitIdx = group.unitIndexMap.get(e.unit.id);
|
||||
if (unitIdx !== undefined) continue;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -50,5 +50,11 @@ export const InteractionsRepresentationProvider = StructureRepresentationProvide
|
||||
ensureCustomProperties: {
|
||||
attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true),
|
||||
detach: (data) => InteractionsProvider.ref(data, false)
|
||||
},
|
||||
getData: (structure: Structure, props: PD.Values<InteractionsParams>) => {
|
||||
return props.includeParent ? structure.asParent() : structure;
|
||||
},
|
||||
mustRecreate: (oldProps: PD.Values<InteractionsParams>, newProps: PD.Values<InteractionsParams>) => {
|
||||
return oldProps.includeParent !== newProps.includeParent;
|
||||
}
|
||||
});
|
||||
@@ -43,7 +43,7 @@ export function atomicSequence(): StructureQuery {
|
||||
|
||||
units.push(unit);
|
||||
}
|
||||
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
|
||||
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export function water(): StructureQuery {
|
||||
if (P.entity.type(l) !== 'water') continue;
|
||||
units.push(unit);
|
||||
}
|
||||
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
|
||||
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ export function atomicHet(): StructureQuery {
|
||||
|
||||
units.push(unit);
|
||||
}
|
||||
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
|
||||
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ export function spheres(): StructureQuery {
|
||||
if (unit.kind !== Unit.Kind.Spheres) continue;
|
||||
units.push(unit);
|
||||
}
|
||||
return StructureSelection.Singletons(inputStructure, new Structure(units, { parent: inputStructure }));
|
||||
return StructureSelection.Singletons(inputStructure, Structure.create(units, { parent: inputStructure }));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,91 +35,73 @@ import { Trajectory } from '../trajectory';
|
||||
import { RuntimeContext, Task } from '../../../mol-task';
|
||||
import { computeStructureBoundary } from './util/boundary';
|
||||
|
||||
/** Internal structure state */
|
||||
type State = {
|
||||
parent?: Structure,
|
||||
boundary?: Boundary,
|
||||
lookup3d?: StructureLookup3D,
|
||||
interUnitBonds?: InterUnitBonds,
|
||||
unitSymmetryGroups?: ReadonlyArray<Unit.SymmetryGroup>,
|
||||
unitSymmetryGroupsIndexMap?: IntMap<number>,
|
||||
unitsSortedByVolume?: ReadonlyArray<Unit>;
|
||||
carbohydrates?: Carbohydrates,
|
||||
models?: ReadonlyArray<Model>,
|
||||
model?: Model,
|
||||
masterModel?: Model,
|
||||
representativeModel?: Model,
|
||||
uniqueResidueNames?: Set<string>,
|
||||
uniqueElementSymbols?: Set<ElementSymbol>,
|
||||
entityIndices?: ReadonlyArray<EntityIndex>,
|
||||
uniqueAtomicResidueIndices?: ReadonlyMap<UUID, ReadonlyArray<ResidueIndex>>,
|
||||
serialMapping?: SerialMapping,
|
||||
hashCode: number,
|
||||
transformHash: number,
|
||||
elementCount: number,
|
||||
bondCount: number,
|
||||
uniqueElementCount: number,
|
||||
atomicResidueCount: number,
|
||||
polymerResidueCount: number,
|
||||
polymerGapCount: number,
|
||||
polymerUnitCount: number,
|
||||
coordinateSystem: SymmetryOperator,
|
||||
label: string,
|
||||
propertyData?: any,
|
||||
customProps?: CustomProperties
|
||||
}
|
||||
|
||||
class Structure {
|
||||
/** Maps unit.id to unit */
|
||||
readonly unitMap: IntMap<Unit>;
|
||||
/** Maps unit.id to index of unit in units array */
|
||||
readonly unitIndexMap: IntMap<number>;
|
||||
/** Array of all units in the structure, sorted by unit.id */
|
||||
readonly units: ReadonlyArray<Unit>;
|
||||
|
||||
private _props: {
|
||||
parent?: Structure,
|
||||
boundary?: Boundary,
|
||||
lookup3d?: StructureLookup3D,
|
||||
interUnitBonds?: InterUnitBonds,
|
||||
unitSymmetryGroups?: ReadonlyArray<Unit.SymmetryGroup>,
|
||||
unitSymmetryGroupsIndexMap?: IntMap<number>,
|
||||
carbohydrates?: Carbohydrates,
|
||||
models?: ReadonlyArray<Model>,
|
||||
model?: Model,
|
||||
masterModel?: Model,
|
||||
representativeModel?: Model,
|
||||
uniqueResidueNames?: Set<string>,
|
||||
uniqueElementSymbols?: Set<ElementSymbol>,
|
||||
entityIndices?: ReadonlyArray<EntityIndex>,
|
||||
uniqueAtomicResidueIndices?: ReadonlyMap<UUID, ReadonlyArray<ResidueIndex>>,
|
||||
serialMapping?: SerialMapping,
|
||||
hashCode: number,
|
||||
/** Hash based on all unit.id values in the structure, reflecting the units transformation */
|
||||
transformHash: number,
|
||||
elementCount: number,
|
||||
bondCount: number,
|
||||
uniqueElementCount: number,
|
||||
atomicResidueCount: number,
|
||||
polymerResidueCount: number,
|
||||
polymerGapCount: number,
|
||||
polymerUnitCount: number,
|
||||
coordinateSystem: SymmetryOperator,
|
||||
label: string,
|
||||
propertyData?: any,
|
||||
customProps?: CustomProperties
|
||||
} = {
|
||||
hashCode: -1,
|
||||
transformHash: -1,
|
||||
elementCount: -1,
|
||||
bondCount: -1,
|
||||
uniqueElementCount: -1,
|
||||
atomicResidueCount: -1,
|
||||
polymerResidueCount: -1,
|
||||
polymerGapCount: -1,
|
||||
polymerUnitCount: -1,
|
||||
coordinateSystem: SymmetryOperator.Default,
|
||||
label: ''
|
||||
};
|
||||
|
||||
subsetBuilder(isSorted: boolean) {
|
||||
return new StructureSubsetBuilder(this, isSorted);
|
||||
}
|
||||
|
||||
/** Count of all elements in the structure, i.e. the sum of the elements in the units */
|
||||
get elementCount() {
|
||||
return this._props.elementCount;
|
||||
return this.state.elementCount;
|
||||
}
|
||||
|
||||
/** Count of all bonds (intra- and inter-unit) in the structure */
|
||||
get bondCount() {
|
||||
if (this._props.bondCount === -1) {
|
||||
this._props.bondCount = this.interUnitBonds.edgeCount + Bond.getIntraUnitBondCount(this);
|
||||
if (this.state.bondCount === -1) {
|
||||
this.state.bondCount = this.interUnitBonds.edgeCount + Bond.getIntraUnitBondCount(this);
|
||||
}
|
||||
return this._props.bondCount;
|
||||
return this.state.bondCount;
|
||||
}
|
||||
|
||||
get hasCustomProperties() {
|
||||
return !!this._props.customProps && this._props.customProps.all.length > 0;
|
||||
return !!this.state.customProps && this.state.customProps.all.length > 0;
|
||||
}
|
||||
|
||||
get customPropertyDescriptors() {
|
||||
if (!this._props.customProps) this._props.customProps = new CustomProperties();
|
||||
return this._props.customProps;
|
||||
if (!this.state.customProps) this.state.customProps = new CustomProperties();
|
||||
return this.state.customProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Property data unique to this instance of the structure.
|
||||
*/
|
||||
get currentPropertyData() {
|
||||
if (!this._props.propertyData) this._props.propertyData = Object.create(null);
|
||||
return this._props.propertyData;
|
||||
if (!this.state.propertyData) this.state.propertyData = Object.create(null);
|
||||
return this.state.propertyData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,39 +113,39 @@ class Structure {
|
||||
|
||||
/** Count of all polymer residues in the structure */
|
||||
get polymerResidueCount() {
|
||||
if (this._props.polymerResidueCount === -1) {
|
||||
this._props.polymerResidueCount = getPolymerResidueCount(this);
|
||||
if (this.state.polymerResidueCount === -1) {
|
||||
this.state.polymerResidueCount = getPolymerResidueCount(this);
|
||||
}
|
||||
return this._props.polymerResidueCount;
|
||||
return this.state.polymerResidueCount;
|
||||
}
|
||||
|
||||
/** Count of all polymer gaps in the structure */
|
||||
get polymerGapCount() {
|
||||
if (this._props.polymerGapCount === -1) {
|
||||
this._props.polymerGapCount = getPolymerGapCount(this);
|
||||
if (this.state.polymerGapCount === -1) {
|
||||
this.state.polymerGapCount = getPolymerGapCount(this);
|
||||
}
|
||||
return this._props.polymerGapCount;
|
||||
return this.state.polymerGapCount;
|
||||
}
|
||||
|
||||
get polymerUnitCount() {
|
||||
if (this._props.polymerUnitCount === -1) {
|
||||
this._props.polymerUnitCount = getPolymerUnitCount(this);
|
||||
if (this.state.polymerUnitCount === -1) {
|
||||
this.state.polymerUnitCount = getPolymerUnitCount(this);
|
||||
}
|
||||
return this._props.polymerUnitCount;
|
||||
return this.state.polymerUnitCount;
|
||||
}
|
||||
|
||||
get uniqueElementCount() {
|
||||
if (this._props.uniqueElementCount === -1) {
|
||||
this._props.uniqueElementCount = getUniqueElementCount(this);
|
||||
if (this.state.uniqueElementCount === -1) {
|
||||
this.state.uniqueElementCount = getUniqueElementCount(this);
|
||||
}
|
||||
return this._props.uniqueElementCount;
|
||||
return this.state.uniqueElementCount;
|
||||
}
|
||||
|
||||
get atomicResidueCount() {
|
||||
if (this._props.atomicResidueCount === -1) {
|
||||
this._props.atomicResidueCount = getAtomicResidueCount(this);
|
||||
if (this.state.atomicResidueCount === -1) {
|
||||
this.state.atomicResidueCount = getAtomicResidueCount(this);
|
||||
}
|
||||
return this._props.atomicResidueCount;
|
||||
return this.state.atomicResidueCount;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,15 +161,15 @@ class Structure {
|
||||
}
|
||||
|
||||
get hashCode() {
|
||||
if (this._props.hashCode !== -1) return this._props.hashCode;
|
||||
if (this.state.hashCode !== -1) return this.state.hashCode;
|
||||
return this.computeHash();
|
||||
}
|
||||
|
||||
/** Hash based on all unit.id values in the structure, reflecting the units transformation */
|
||||
get transformHash() {
|
||||
if (this._props.transformHash !== -1) return this._props.transformHash;
|
||||
this._props.transformHash = hashFnv32a(this.units.map(u => u.id));
|
||||
return this._props.transformHash;
|
||||
if (this.state.transformHash !== -1) return this.state.transformHash;
|
||||
this.state.transformHash = hashFnv32a(this.units.map(u => u.id));
|
||||
return this.state.transformHash;
|
||||
}
|
||||
|
||||
private computeHash() {
|
||||
@@ -200,7 +182,7 @@ class Structure {
|
||||
hash = (31 * hash + this.elementCount) | 0;
|
||||
hash = hash1(hash);
|
||||
if (hash === -1) hash = 0;
|
||||
this._props.hashCode = hash;
|
||||
this.state.hashCode = hash;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -211,12 +193,12 @@ class Structure {
|
||||
|
||||
/** The parent or itself in case this is the root */
|
||||
get root() {
|
||||
return this._props.parent || this;
|
||||
return this.state.parent || this;
|
||||
}
|
||||
|
||||
/** The root/top-most parent or `undefined` in case this is the root */
|
||||
get parent() {
|
||||
return this._props.parent;
|
||||
return this.state.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,74 +209,74 @@ class Structure {
|
||||
* by the consumer.
|
||||
*/
|
||||
get coordinateSystem(): SymmetryOperator {
|
||||
return this._props.coordinateSystem;
|
||||
return this.state.coordinateSystem;
|
||||
}
|
||||
|
||||
get label() {
|
||||
return this._props.label;
|
||||
return this.state.label;
|
||||
}
|
||||
|
||||
get boundary() {
|
||||
if (this._props.boundary) return this._props.boundary;
|
||||
this._props.boundary = computeStructureBoundary(this);
|
||||
return this._props.boundary;
|
||||
if (this.state.boundary) return this.state.boundary;
|
||||
this.state.boundary = computeStructureBoundary(this);
|
||||
return this.state.boundary;
|
||||
}
|
||||
|
||||
get lookup3d() {
|
||||
if (this._props.lookup3d) return this._props.lookup3d;
|
||||
this._props.lookup3d = new StructureLookup3D(this);
|
||||
return this._props.lookup3d;
|
||||
if (this.state.lookup3d) return this.state.lookup3d;
|
||||
this.state.lookup3d = new StructureLookup3D(this);
|
||||
return this.state.lookup3d;
|
||||
}
|
||||
|
||||
get interUnitBonds() {
|
||||
if (this._props.interUnitBonds) return this._props.interUnitBonds;
|
||||
this._props.interUnitBonds = computeInterUnitBonds(this);
|
||||
return this._props.interUnitBonds;
|
||||
if (this.state.interUnitBonds) return this.state.interUnitBonds;
|
||||
this.state.interUnitBonds = computeInterUnitBonds(this);
|
||||
return this.state.interUnitBonds;
|
||||
}
|
||||
|
||||
get unitSymmetryGroups(): ReadonlyArray<Unit.SymmetryGroup> {
|
||||
if (this._props.unitSymmetryGroups) return this._props.unitSymmetryGroups;
|
||||
this._props.unitSymmetryGroups = StructureSymmetry.computeTransformGroups(this);
|
||||
return this._props.unitSymmetryGroups;
|
||||
if (this.state.unitSymmetryGroups) return this.state.unitSymmetryGroups;
|
||||
this.state.unitSymmetryGroups = StructureSymmetry.computeTransformGroups(this);
|
||||
return this.state.unitSymmetryGroups;
|
||||
}
|
||||
|
||||
/** Maps unit.id to index of SymmetryGroup in unitSymmetryGroups array */
|
||||
get unitSymmetryGroupsIndexMap(): IntMap<number> {
|
||||
if (this._props.unitSymmetryGroupsIndexMap) return this._props.unitSymmetryGroupsIndexMap;
|
||||
this._props.unitSymmetryGroupsIndexMap = Unit.SymmetryGroup.getUnitSymmetryGroupsIndexMap(this.unitSymmetryGroups);
|
||||
return this._props.unitSymmetryGroupsIndexMap;
|
||||
if (this.state.unitSymmetryGroupsIndexMap) return this.state.unitSymmetryGroupsIndexMap;
|
||||
this.state.unitSymmetryGroupsIndexMap = Unit.SymmetryGroup.getUnitSymmetryGroupsIndexMap(this.unitSymmetryGroups);
|
||||
return this.state.unitSymmetryGroupsIndexMap;
|
||||
}
|
||||
|
||||
get carbohydrates(): Carbohydrates {
|
||||
if (this._props.carbohydrates) return this._props.carbohydrates;
|
||||
this._props.carbohydrates = computeCarbohydrates(this);
|
||||
return this._props.carbohydrates;
|
||||
if (this.state.carbohydrates) return this.state.carbohydrates;
|
||||
this.state.carbohydrates = computeCarbohydrates(this);
|
||||
return this.state.carbohydrates;
|
||||
}
|
||||
|
||||
get models(): ReadonlyArray<Model> {
|
||||
if (this._props.models) return this._props.models;
|
||||
this._props.models = getModels(this);
|
||||
return this._props.models;
|
||||
if (this.state.models) return this.state.models;
|
||||
this.state.models = getModels(this);
|
||||
return this.state.models;
|
||||
}
|
||||
|
||||
get uniqueResidueNames() {
|
||||
return this._props.uniqueResidueNames
|
||||
|| (this._props.uniqueResidueNames = getUniqueResidueNames(this));
|
||||
return this.state.uniqueResidueNames
|
||||
|| (this.state.uniqueResidueNames = getUniqueResidueNames(this));
|
||||
}
|
||||
|
||||
get uniqueElementSymbols() {
|
||||
return this._props.uniqueElementSymbols
|
||||
|| (this._props.uniqueElementSymbols = getUniqueElementSymbols(this));
|
||||
return this.state.uniqueElementSymbols
|
||||
|| (this.state.uniqueElementSymbols = getUniqueElementSymbols(this));
|
||||
}
|
||||
|
||||
get entityIndices() {
|
||||
return this._props.entityIndices
|
||||
|| (this._props.entityIndices = getEntityIndices(this));
|
||||
return this.state.entityIndices
|
||||
|| (this.state.entityIndices = getEntityIndices(this));
|
||||
}
|
||||
|
||||
get uniqueAtomicResidueIndices() {
|
||||
return this._props.uniqueAtomicResidueIndices
|
||||
|| (this._props.uniqueAtomicResidueIndices = getUniqueAtomicResidueIndices(this));
|
||||
return this.state.uniqueAtomicResidueIndices
|
||||
|| (this.state.uniqueAtomicResidueIndices = getUniqueAtomicResidueIndices(this));
|
||||
}
|
||||
|
||||
/** Contains only atomic units */
|
||||
@@ -329,7 +311,7 @@ class Structure {
|
||||
* to address elements in a structure.
|
||||
*/
|
||||
get serialMapping() {
|
||||
return this._props.serialMapping || (this._props.serialMapping = getSerialMapping(this));
|
||||
return this.state.serialMapping || (this.state.serialMapping = getSerialMapping(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,25 +319,25 @@ class Structure {
|
||||
* Otherwise throw an exception.
|
||||
*/
|
||||
get model(): Model {
|
||||
if (this._props.model) return this._props.model;
|
||||
if (this._props.representativeModel) return this._props.representativeModel;
|
||||
if (this._props.masterModel) return this._props.masterModel;
|
||||
if (this.state.model) return this.state.model;
|
||||
if (this.state.representativeModel) return this.state.representativeModel;
|
||||
if (this.state.masterModel) return this.state.masterModel;
|
||||
const models = this.models;
|
||||
if (models.length > 1) {
|
||||
throw new Error('The structure is based on multiple models and has neither a master- nor a representative-model.');
|
||||
}
|
||||
this._props.model = models[0];
|
||||
return this._props.model;
|
||||
this.state.model = models[0];
|
||||
return this.state.model;
|
||||
}
|
||||
|
||||
/** The master-model, other models can have bonds to it */
|
||||
get masterModel(): Model | undefined {
|
||||
return this._props.masterModel;
|
||||
return this.state.masterModel;
|
||||
}
|
||||
|
||||
/** A representative model, e.g. the first model of a trajectory */
|
||||
get representativeModel(): Model | undefined {
|
||||
return this._props.representativeModel;
|
||||
return this.state.representativeModel;
|
||||
}
|
||||
|
||||
hasElement(e: StructureElement.Location) {
|
||||
@@ -379,51 +361,39 @@ class Structure {
|
||||
}
|
||||
return Structure.create(units, {
|
||||
label: this.label,
|
||||
interUnitBonds: this._props.interUnitBonds,
|
||||
interUnitBonds: this.state.interUnitBonds,
|
||||
});
|
||||
}
|
||||
|
||||
private initUnits(units: ArrayLike<Unit>) {
|
||||
const unitMap = IntMap.Mutable<Unit>();
|
||||
const unitIndexMap = IntMap.Mutable<number>();
|
||||
let elementCount = 0;
|
||||
let isSorted = true;
|
||||
let lastId = units.length > 0 ? units[0].id : 0;
|
||||
for (let i = 0, _i = units.length; i < _i; i++) {
|
||||
const u = units[i];
|
||||
unitMap.set(u.id, u);
|
||||
elementCount += u.elements.length;
|
||||
if (u.id < lastId) isSorted = false;
|
||||
lastId = u.id;
|
||||
}
|
||||
if (!isSorted) sort(units, 0, units.length, cmpUnits, arraySwap);
|
||||
for (let i = 0, _i = units.length; i < _i; i++) {
|
||||
unitIndexMap.set(units[i].id, i);
|
||||
}
|
||||
this._props.elementCount = elementCount;
|
||||
return { unitMap, unitIndexMap };
|
||||
private _child: Structure | undefined;
|
||||
private _target: Structure | undefined;
|
||||
|
||||
/**
|
||||
* For `structure` with `parent` this returns a proxy that
|
||||
* targets `parent` and has `structure` attached as a child.
|
||||
*/
|
||||
asParent(): Structure {
|
||||
return this.parent ? new Structure(this.parent.units, this.parent.unitMap, this.parent.unitIndexMap, this.parent.state, { child: this, target: this.parent }) : this;
|
||||
}
|
||||
|
||||
constructor(units: ArrayLike<Unit>, props: Structure.Props = {}) {
|
||||
const { unitMap, unitIndexMap } = this.initUnits(units);
|
||||
this.unitMap = unitMap;
|
||||
this.unitIndexMap = unitIndexMap;
|
||||
this.units = units as ReadonlyArray<Unit>;
|
||||
get child(): Structure | undefined {
|
||||
return this._child;
|
||||
}
|
||||
|
||||
if (props.parent) this._props.parent = props.parent.parent || props.parent;
|
||||
if (props.interUnitBonds) this._props.interUnitBonds = props.interUnitBonds;
|
||||
/** Get the proxy target. Usefull for equality checks. */
|
||||
get target(): Structure {
|
||||
return this._target ?? this;
|
||||
}
|
||||
|
||||
if (props.coordinateSystem) this._props.coordinateSystem = props.coordinateSystem;
|
||||
else if (props.parent) this._props.coordinateSystem = props.parent.coordinateSystem;
|
||||
|
||||
if (props.label) this._props.label = props.label;
|
||||
else if (props.parent) this._props.label = props.parent.label;
|
||||
|
||||
if (props.masterModel) this._props.masterModel = props.masterModel;
|
||||
else if (props.parent) this._props.masterModel = props.parent.masterModel;
|
||||
|
||||
if (props.representativeModel) this._props.representativeModel = props.representativeModel;
|
||||
else if (props.parent) this._props.representativeModel = props.parent.representativeModel;
|
||||
/**
|
||||
* @param units Array of all units in the structure, sorted by unit.id
|
||||
* @param unitMap Maps unit.id to index of unit in units array
|
||||
* @param unitIndexMap Array of all units in the structure, sorted by unit.id
|
||||
*/
|
||||
constructor(readonly units: ReadonlyArray<Unit>, readonly unitMap: IntMap<Unit>, readonly unitIndexMap: IntMap<number>, private readonly state: State, asParent?: { child: Structure, target: Structure }) {
|
||||
// always assign to ensure object shape
|
||||
this._child = asParent?.child;
|
||||
this._target = asParent?.target;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,7 +595,7 @@ function getSerialMapping(structure: Structure): SerialMapping {
|
||||
}
|
||||
|
||||
namespace Structure {
|
||||
export const Empty = new Structure([]);
|
||||
export const Empty = create([]);
|
||||
|
||||
export interface Props {
|
||||
parent?: Structure
|
||||
@@ -638,7 +608,7 @@ namespace Structure {
|
||||
representativeModel?: Model
|
||||
}
|
||||
|
||||
/** Serial index of an element in the structure accross all units */
|
||||
/** Serial index of an element in the structure across all units */
|
||||
export type SerialIndex = { readonly '@type': 'serial-index' } & number
|
||||
|
||||
/** Represents a single structure */
|
||||
@@ -679,8 +649,57 @@ namespace Structure {
|
||||
return Loci(structure);
|
||||
}
|
||||
|
||||
export function create(units: ReadonlyArray<Unit>, props?: Props): Structure {
|
||||
return new Structure(units, props);
|
||||
export function create(units: ReadonlyArray<Unit>, props: Props = {}): Structure {
|
||||
// init units
|
||||
const unitMap = IntMap.Mutable<Unit>();
|
||||
const unitIndexMap = IntMap.Mutable<number>();
|
||||
let elementCount = 0;
|
||||
let isSorted = true;
|
||||
let lastId = units.length > 0 ? units[0].id : 0;
|
||||
for (let i = 0, _i = units.length; i < _i; i++) {
|
||||
const u = units[i];
|
||||
unitMap.set(u.id, u);
|
||||
elementCount += u.elements.length;
|
||||
if (u.id < lastId) isSorted = false;
|
||||
lastId = u.id;
|
||||
}
|
||||
if (!isSorted) sort(units, 0, units.length, cmpUnits, arraySwap);
|
||||
for (let i = 0, _i = units.length; i < _i; i++) {
|
||||
unitIndexMap.set(units[i].id, i);
|
||||
}
|
||||
|
||||
// initial state
|
||||
const state: State = {
|
||||
hashCode: -1,
|
||||
transformHash: -1,
|
||||
elementCount,
|
||||
bondCount: -1,
|
||||
uniqueElementCount: -1,
|
||||
atomicResidueCount: -1,
|
||||
polymerResidueCount: -1,
|
||||
polymerGapCount: -1,
|
||||
polymerUnitCount: -1,
|
||||
coordinateSystem: SymmetryOperator.Default,
|
||||
label: ''
|
||||
};
|
||||
|
||||
// handle props
|
||||
if (props.parent) state.parent = props.parent.parent || props.parent;
|
||||
if (props.interUnitBonds) state.interUnitBonds = props.interUnitBonds;
|
||||
|
||||
if (props.coordinateSystem) state.coordinateSystem = props.coordinateSystem;
|
||||
else if (props.parent) state.coordinateSystem = props.parent.coordinateSystem;
|
||||
|
||||
if (props.label) state.label = props.label;
|
||||
else if (props.parent) state.label = props.parent.label;
|
||||
|
||||
if (props.masterModel) state.masterModel = props.masterModel;
|
||||
else if (props.parent) state.masterModel = props.parent.masterModel;
|
||||
|
||||
if (props.representativeModel) state.representativeModel = props.representativeModel;
|
||||
else if (props.parent) state.representativeModel = props.parent.representativeModel;
|
||||
|
||||
return new Structure(units, unitMap, unitIndexMap, state);
|
||||
}
|
||||
|
||||
export async function ofTrajectory(trajectory: Trajectory, ctx: RuntimeContext): Promise<Structure> {
|
||||
@@ -886,7 +905,7 @@ namespace Structure {
|
||||
|
||||
const cs = s.coordinateSystem;
|
||||
const newCS = SymmetryOperator.compose(SymmetryOperator.create(cs.name, transform, cs), cs);
|
||||
return new Structure(units, { parent: s, coordinateSystem: newCS });
|
||||
return create(units, { parent: s, coordinateSystem: newCS });
|
||||
}
|
||||
|
||||
export class StructureBuilder {
|
||||
|
||||
@@ -21,7 +21,7 @@ export async function setStructureClipping(plugin: PluginContext, components: St
|
||||
await eachRepr(plugin, components, async (update, repr, clippingCell) => {
|
||||
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
|
||||
|
||||
const structure = repr.obj!.data.source.data;
|
||||
const structure = repr.obj!.data.sourceData;
|
||||
// always use the root structure to get the loci so the clipping
|
||||
// stays applicable as long as the root structure does not change
|
||||
const loci = await lociGetter(structure.root);
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function setStructureOverpaint(plugin: PluginContext, components: S
|
||||
await eachRepr(plugin, components, async (update, repr, overpaintCell) => {
|
||||
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
|
||||
|
||||
const structure = repr.obj!.data.source.data;
|
||||
const structure = repr.obj!.data.sourceData;
|
||||
// always use the root structure to get the loci so the overpaint
|
||||
// stays applicable as long as the root structure does not change
|
||||
const loci = await lociGetter(structure.root);
|
||||
|
||||
@@ -21,7 +21,7 @@ export async function setStructureTransparency(plugin: PluginContext, components
|
||||
await eachRepr(plugin, components, async (update, repr, transparencyCell) => {
|
||||
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
|
||||
|
||||
const structure = repr.obj!.data.source.data;
|
||||
const structure = repr.obj!.data.sourceData;
|
||||
// always use the root structure to get the loci so the transparency
|
||||
// stays applicable as long as the root structure does not change
|
||||
const loci = await lociGetter(structure.root);
|
||||
|
||||
@@ -41,8 +41,8 @@ export namespace PluginStateObject {
|
||||
return !!o && o.type.typeClass === 'Behavior';
|
||||
}
|
||||
|
||||
export interface Representation3DData<T extends Representation.Any, S extends StateObject = StateObject> { repr: T, source: S }
|
||||
export function CreateRepresentation3D<T extends Representation.Any, S extends StateObject = StateObject>(type: { name: string }) {
|
||||
export interface Representation3DData<T extends Representation.Any, S = any> { repr: T, sourceData: S }
|
||||
export function CreateRepresentation3D<T extends Representation.Any, S = any>(type: { name: string }) {
|
||||
return Create<Representation3DData<T, S>>({ ...type, typeClass: 'Representation3D' });
|
||||
}
|
||||
|
||||
@@ -102,10 +102,10 @@ export namespace PluginStateObject {
|
||||
export class Structure extends Create<_Structure>({ name: 'Structure', typeClass: 'Object' }) { }
|
||||
|
||||
export namespace Structure {
|
||||
export class Representation3D extends CreateRepresentation3D<StructureRepresentation<any> | ShapeRepresentation<any, any, any>, Structure>({ name: 'Structure 3D' }) { }
|
||||
export class Representation3D extends CreateRepresentation3D<StructureRepresentation<any>, _Structure>({ name: 'Structure 3D' }) { }
|
||||
|
||||
export interface Representation3DStateData {
|
||||
source: Representation3D,
|
||||
repr: StructureRepresentation<any>,
|
||||
/** used to restore state when the obj is removed */
|
||||
initialState: Partial<StructureRepresentationState>,
|
||||
state: Partial<StructureRepresentationState>,
|
||||
@@ -120,12 +120,12 @@ export namespace PluginStateObject {
|
||||
|
||||
export namespace Volume {
|
||||
export class Data extends Create<_Volume>({ name: 'Volume', typeClass: 'Object' }) { }
|
||||
export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>>({ name: 'Volume 3D' }) { }
|
||||
export class Representation3D extends CreateRepresentation3D<VolumeRepresentation<any>, _Volume>({ name: 'Volume 3D' }) { }
|
||||
}
|
||||
|
||||
export namespace Shape {
|
||||
export class Provider extends Create<ShapeProvider<any, any, any>>({ name: 'Shape Provider', typeClass: 'Object' }) { }
|
||||
export class Representation3D extends CreateRepresentation3D<ShapeRepresentation<any, any, any>>({ name: 'Shape 3D' }) { }
|
||||
export class Representation3D extends CreateRepresentation3D<ShapeRepresentation<any, any, any>, unknown>({ name: 'Shape 3D' }) { }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,14 +130,15 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
return Task.create('Structure Representation', async ctx => {
|
||||
const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
|
||||
const provider = plugin.representation.structure.registry.get(params.type.name);
|
||||
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data);
|
||||
const data = provider.getData?.(a.data, params.type.params) || a.data;
|
||||
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, data);
|
||||
const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, provider.getParams);
|
||||
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: a.data }, params);
|
||||
repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: a.data }, params));
|
||||
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: data }, params);
|
||||
repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: data }, params));
|
||||
|
||||
const props = params.type.params || {};
|
||||
await repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
return new SO.Molecule.Structure.Representation3D({ repr, source: a }, { label: provider.label });
|
||||
await repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
return new SO.Molecule.Structure.Representation3D({ repr, sourceData: a.data }, { label: provider.label });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams, cache }, plugin: PluginContext) {
|
||||
@@ -145,26 +146,28 @@ const StructureRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
if (newParams.type.name !== oldParams.type.name) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const provider = plugin.representation.structure.registry.get(newParams.type.name);
|
||||
if (provider.mustRecreate?.(oldParams.type.params, newParams.type.params)) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const data = provider.getData?.(a.data, newParams.type.params) || a.data;
|
||||
const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
|
||||
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data);
|
||||
if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, data);
|
||||
|
||||
// TODO: if themes had a .needsUpdate method the following block could
|
||||
// be optimized and only executed conditionally
|
||||
// dispose isn't called on update so we need to handle it manually
|
||||
Theme.releaseDependencies(plugin.representation.structure.themes, { structure: b.data.source.data }, oldParams);
|
||||
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: a.data }, newParams);
|
||||
b.data.repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: a.data }, newParams));
|
||||
Theme.releaseDependencies(plugin.representation.structure.themes, { structure: b.data.sourceData }, oldParams);
|
||||
await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: data }, newParams);
|
||||
b.data.repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: data }, newParams));
|
||||
|
||||
const props = { ...b.data.repr.props, ...newParams.type.params };
|
||||
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
b.data.sourceData = a.data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
dispose({ b, params }, plugin: PluginContext) {
|
||||
if (!b || !params) return;
|
||||
|
||||
const structure = b.data.source.data;
|
||||
const structure = b.data.sourceData;
|
||||
const provider = plugin.representation.structure.registry.get(params.type.name);
|
||||
if (provider.ensureCustomProperties) provider.ensureCustomProperties.detach(structure);
|
||||
Theme.releaseDependencies(plugin.representation.structure.themes, { structure }, params);
|
||||
@@ -195,26 +198,26 @@ const UnwindStructureAssemblyRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const unitTransforms = new StructureUnitTransforms(structure);
|
||||
unwindStructureAssembly(structure, unitTransforms, params.t);
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { unitTransforms },
|
||||
initialState: { unitTransforms: new StructureUnitTransforms(structure) },
|
||||
info: structure,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Unwind T = ${params.t.toFixed(2)}` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = b.data.info as Structure;
|
||||
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
|
||||
const unitTransforms = b.data.state.unitTransforms!;
|
||||
unwindStructureAssembly(structure, unitTransforms, newParams.t);
|
||||
b.label = `Unwind T = ${newParams.t.toFixed(2)}`;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
});
|
||||
@@ -232,26 +235,26 @@ const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const unitTransforms = new StructureUnitTransforms(structure.root);
|
||||
explodeStructure(structure, unitTransforms, params.t);
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { unitTransforms },
|
||||
initialState: { unitTransforms: new StructureUnitTransforms(structure.root) },
|
||||
info: structure.root,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Explode T = ${params.t.toFixed(2)}` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
if (b.data.info !== structure.root) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
|
||||
const unitTransforms = b.data.state.unitTransforms!;
|
||||
explodeStructure(structure.root, unitTransforms, newParams.t);
|
||||
b.label = `Explode T = ${newParams.t.toFixed(2)}`;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
});
|
||||
@@ -280,28 +283,28 @@ const OverpaintStructureRepresentation3DFromScript = PluginStateTransform.BuiltI
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const overpaint = Overpaint.ofScript(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { overpaint },
|
||||
initialState: { overpaint: Overpaint.Empty },
|
||||
info: structure,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Overpaint (${overpaint.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const oldStructure = b.data.info as Structure;
|
||||
const newStructure = a.data.source.data;
|
||||
const newStructure = a.data.sourceData;
|
||||
if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const oldOverpaint = b.data.state.overpaint!;
|
||||
const newOverpaint = Overpaint.ofScript(newParams.layers, newStructure);
|
||||
if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
b.data.state.overpaint = newOverpaint;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
@@ -332,28 +335,28 @@ const OverpaintStructureRepresentation3DFromBundle = PluginStateTransform.BuiltI
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const overpaint = Overpaint.ofBundle(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { overpaint },
|
||||
initialState: { overpaint: Overpaint.Empty },
|
||||
info: structure,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Overpaint (${overpaint.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const oldStructure = b.data.info as Structure;
|
||||
const newStructure = a.data.source.data;
|
||||
const newStructure = a.data.sourceData;
|
||||
if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const oldOverpaint = b.data.state.overpaint!;
|
||||
const newOverpaint = Overpaint.ofBundle(newParams.layers, newStructure);
|
||||
if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
b.data.state.overpaint = newOverpaint;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
@@ -381,27 +384,27 @@ const TransparencyStructureRepresentation3DFromScript = PluginStateTransform.Bui
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const transparency = Transparency.ofScript(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { transparency },
|
||||
initialState: { transparency: Transparency.Empty },
|
||||
info: structure,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Transparency (${transparency.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = b.data.info as Structure;
|
||||
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const oldTransparency = b.data.state.transparency!;
|
||||
const newTransparency = Transparency.ofScript(newParams.layers, structure);
|
||||
if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
b.data.state.transparency = newTransparency;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Transparency (${newTransparency.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
@@ -430,27 +433,27 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const transparency = Transparency.ofBundle(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { transparency },
|
||||
initialState: { transparency: Transparency.Empty },
|
||||
info: structure,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Transparency (${transparency.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = b.data.info as Structure;
|
||||
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const oldTransparency = b.data.state.transparency!;
|
||||
const newTransparency = Transparency.ofBundle(newParams.layers, structure);
|
||||
if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
b.data.state.transparency = newTransparency;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Transparency (${newTransparency.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
@@ -478,27 +481,27 @@ const ClippingStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const clipping = Clipping.ofScript(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { clipping },
|
||||
initialState: { clipping: Clipping.Empty },
|
||||
info: structure,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Clipping (${clipping.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = b.data.info as Structure;
|
||||
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const oldClipping = b.data.state.clipping!;
|
||||
const newClipping = Clipping.ofScript(newParams.layers, structure);
|
||||
if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
b.data.state.clipping = newClipping;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Clipping (${newClipping.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
@@ -527,27 +530,27 @@ const ClippingStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn
|
||||
return true;
|
||||
},
|
||||
apply({ a, params }) {
|
||||
const structure = a.data.source.data;
|
||||
const structure = a.data.sourceData;
|
||||
const clipping = Clipping.ofBundle(params.layers, structure);
|
||||
|
||||
return new SO.Molecule.Structure.Representation3DState({
|
||||
state: { clipping },
|
||||
initialState: { clipping: Clipping.Empty },
|
||||
info: structure,
|
||||
source: a
|
||||
repr: a.data.repr
|
||||
}, { label: `Clipping (${clipping.layers.length} Layers)` });
|
||||
},
|
||||
update({ a, b, newParams, oldParams }) {
|
||||
const structure = b.data.info as Structure;
|
||||
if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.source.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
|
||||
if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
|
||||
|
||||
const oldClipping = b.data.state.clipping!;
|
||||
const newClipping = Clipping.ofBundle(newParams.layers, structure);
|
||||
if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
|
||||
|
||||
b.data.state.clipping = newClipping;
|
||||
b.data.source = a;
|
||||
b.data.repr = a.data.repr;
|
||||
b.label = `Clipping (${newClipping.layers.length} Layers)`;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
@@ -650,7 +653,7 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
|
||||
const props = params.type.params || {};
|
||||
await repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
return new SO.Volume.Representation3D({ repr, source: a }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
|
||||
return new SO.Volume.Representation3D({ repr, sourceData: a.data }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
@@ -663,6 +666,7 @@ const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams.type.params };
|
||||
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, newParams));
|
||||
await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
|
||||
b.data.sourceData = a.data;
|
||||
b.description = VolumeRepresentation3DHelpers.getDescription(props);
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
@@ -690,13 +694,14 @@ const ShapeRepresentation3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...PD.getDefaultValues(a.data.params), ...params };
|
||||
const repr = ShapeRepresentation(a.data.getShape, a.data.geometryUtils);
|
||||
await repr.createOrUpdate(props, a.data.data).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: a.data.label });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: a.data }, { label: a.data.label });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
return Task.create('Shape Representation', async ctx => {
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
await b.data.repr.createOrUpdate(props, a.data.data).runInContext(ctx);
|
||||
b.data.sourceData = a.data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
}
|
||||
@@ -724,7 +729,7 @@ const ModelUnitcell3D = PluginStateTransform.BuiltIn({
|
||||
const data = getUnitcellData(a.data, symmetry, params);
|
||||
const repr = UnitcellRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => UnitcellParams);
|
||||
await repr.createOrUpdate(params, data).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Unit Cell`, description: symmetry.spacegroup.name });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Unit Cell`, description: symmetry.spacegroup.name });
|
||||
});
|
||||
},
|
||||
update({ a, b, newParams }) {
|
||||
@@ -734,7 +739,7 @@ const ModelUnitcell3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
const data = getUnitcellData(a.data, symmetry, props);
|
||||
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
b.data.sourceData = data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
}
|
||||
@@ -763,13 +768,13 @@ const StructureBoundingBox3D = PluginStateTransform.BuiltIn({
|
||||
return Shape.create('Bouding Box', data, mesh, () => data.color, () => 1, () => 'Bounding Box');
|
||||
}, Mesh.Utils);
|
||||
await repr.createOrUpdate(params, { box: a.data.boundary.box, radius: params.radius, color: params.color }).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Bounding Box` });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: a.data }, { label: `Bounding Box` });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
return Task.create('Bounding Box', async ctx => {
|
||||
await b.data.repr.createOrUpdate(newParams, { box: a.data.boundary.box, radius: newParams.radius, color: newParams.color }).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
b.data.sourceData = a.data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
}
|
||||
@@ -794,7 +799,7 @@ const StructureSelectionsDistance3D = PluginStateTransform.BuiltIn({
|
||||
const data = getDistanceDataFromStructureSelections(a.data);
|
||||
const repr = DistanceRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DistanceParams);
|
||||
await repr.createOrUpdate(params, data).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Distance` });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Distance` });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
@@ -802,7 +807,7 @@ const StructureSelectionsDistance3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
const data = getDistanceDataFromStructureSelections(a.data);
|
||||
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
b.data.sourceData = data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
@@ -827,7 +832,7 @@ const StructureSelectionsAngle3D = PluginStateTransform.BuiltIn({
|
||||
const data = getAngleDataFromStructureSelections(a.data);
|
||||
const repr = AngleRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AngleParams);
|
||||
await repr.createOrUpdate(params, data).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Angle` });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Angle` });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
@@ -835,7 +840,7 @@ const StructureSelectionsAngle3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
const data = getAngleDataFromStructureSelections(a.data);
|
||||
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
b.data.sourceData = data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
@@ -860,7 +865,7 @@ const StructureSelectionsDihedral3D = PluginStateTransform.BuiltIn({
|
||||
const data = getDihedralDataFromStructureSelections(a.data);
|
||||
const repr = DihedralRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DihedralParams);
|
||||
await repr.createOrUpdate(params, data).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Dihedral` });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Dihedral` });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
@@ -868,7 +873,7 @@ const StructureSelectionsDihedral3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
const data = getDihedralDataFromStructureSelections(a.data);
|
||||
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
b.data.sourceData = data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
@@ -893,7 +898,7 @@ const StructureSelectionsLabel3D = PluginStateTransform.BuiltIn({
|
||||
const data = getLabelDataFromStructureSelections(a.data);
|
||||
const repr = LabelRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => LabelParams);
|
||||
await repr.createOrUpdate(params, data).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Label` });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Label` });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
@@ -901,7 +906,7 @@ const StructureSelectionsLabel3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
const data = getLabelDataFromStructureSelections(a.data);
|
||||
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
b.data.sourceData = data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
@@ -926,7 +931,7 @@ const StructureSelectionsOrientation3D = PluginStateTransform.BuiltIn({
|
||||
const data = getOrientationDataFromStructureSelections(a.data);
|
||||
const repr = OrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => OrientationParams);
|
||||
await repr.createOrUpdate(params, data).runInContext(ctx);
|
||||
return new SO.Shape.Representation3D({ repr, source: a }, { label: `Orientation` });
|
||||
return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Orientation` });
|
||||
});
|
||||
},
|
||||
update({ a, b, oldParams, newParams }, plugin: PluginContext) {
|
||||
@@ -934,7 +939,7 @@ const StructureSelectionsOrientation3D = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...newParams };
|
||||
const data = getOrientationDataFromStructureSelections(a.data);
|
||||
await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
|
||||
b.data.source = a;
|
||||
b.data.sourceData = data;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -240,8 +240,8 @@ function renderSimple(options: { props: ParamProps<any>, state: { showHelp: bool
|
||||
const help = props.param.help
|
||||
? props.param.help(props.value)
|
||||
: { description: props.param.description, legend: props.param.legend };
|
||||
const desc = props.param.description;
|
||||
const hasHelp = help.description || help.legend;
|
||||
const desc = label + (hasHelp ? '. Click for help.' : '');
|
||||
return <>
|
||||
<ControlRow
|
||||
className={className}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
import { PluginUIComponent } from '../base';
|
||||
import { StateTransformParameters } from '../state/common';
|
||||
import * as React from 'react';
|
||||
import { VolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/behavior';
|
||||
import { ExpandableControlRow, IconButton } from '../controls/common';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { State } from '../../mol-state';
|
||||
import { PluginUIComponent } from '../base';
|
||||
import { Icon, CodeSvg } from '../controls/icons';
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { PluginUIComponent } from '../base';
|
||||
import { ParameterControls, ParamOnChange } from '../controls/parameters';
|
||||
import { Button } from '../controls/common';
|
||||
|
||||
@@ -8,7 +8,6 @@ import { State, StateTransform, StateTransformer } from '../../mol-state';
|
||||
import { memoizeLatest } from '../../mol-util/memoize';
|
||||
import { StateTransformParameters, TransformControlBase } from './common';
|
||||
import { Observable } from 'rxjs';
|
||||
import * as React from 'react';
|
||||
import { PluginUIComponent } from '../base';
|
||||
|
||||
export { UpdateTransformControl, TransformUpdaterControl };
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { OrderedSet, SortedArray } from '../../mol-data/int';
|
||||
import { Structure, StructureElement, StructureProperties, Unit } from '../../mol-model/structure';
|
||||
import { UnitIndex } from '../../mol-model/structure/structure/element/element';
|
||||
|
||||
@@ -216,7 +216,7 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
}
|
||||
|
||||
get selections() {
|
||||
return this.props.cell.obj?.data.source as PluginStateObject.Molecule.Structure.Selections | undefined;
|
||||
return this.props.cell.obj?.data.sourceData as ReadonlyArray<PluginStateObject.Molecule.Structure.SelectionEntry> | undefined;
|
||||
}
|
||||
|
||||
delete = () => {
|
||||
@@ -234,7 +234,7 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
if (!selections) return;
|
||||
|
||||
this.plugin.managers.interactivity.lociHighlights.clearHighlights();
|
||||
for (const d of selections.data) {
|
||||
for (const d of selections) {
|
||||
this.plugin.managers.interactivity.lociHighlights.highlight({ loci: d.loci }, false);
|
||||
}
|
||||
this.plugin.managers.interactivity.lociHighlights.highlight({ loci: this.props.cell.obj?.data.repr.getLoci()! }, false);
|
||||
@@ -250,7 +250,7 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
const selections = this.selections;
|
||||
if (!selections) return;
|
||||
|
||||
const sphere = Loci.getBundleBoundingSphere(toLociBundle(selections.data));
|
||||
const sphere = Loci.getBundleBoundingSphere(toLociBundle(selections));
|
||||
if (sphere) {
|
||||
this.plugin.managers.camera.focusSphere(sphere);
|
||||
}
|
||||
@@ -258,11 +258,11 @@ class MeasurementEntry extends PurePluginUIComponent<{ cell: StructureMeasuremen
|
||||
|
||||
get label() {
|
||||
const selections = this.selections;
|
||||
switch (selections?.data.length) {
|
||||
case 1: return lociLabel(selections.data[0].loci, { condensed: true });
|
||||
case 2: return distanceLabel(toLociBundle(selections.data), { condensed: true, unitLabel: this.plugin.managers.structure.measurement.state.options.distanceUnitLabel });
|
||||
case 3: return angleLabel(toLociBundle(selections.data), { condensed: true });
|
||||
case 4: return dihedralLabel(toLociBundle(selections.data), { condensed: true });
|
||||
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 '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { Model } from '../../mol-model/structure';
|
||||
import { ModelRef, StructureHierarchyRef, TrajectoryRef } from '../../mol-plugin-state/manager/structure/hierarchy-state';
|
||||
import { StateTransforms } from '../../mol-plugin-state/transforms';
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { CollapsableControls, PurePluginUIComponent } from '../base';
|
||||
import { Icon, ArrowUpwardSvg, ArrowDownwardSvg, DeleteOutlinedSvg, HelpOutlineSvg, TuneSvg, SuperposeAtomsSvg, SuperposeChainsSvg, SuperpositionSvg } from '../controls/icons';
|
||||
import { Button, ToggleButton, IconButton } from '../controls/common';
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { PluginUIComponent } from './base';
|
||||
import { OrderedMap } from 'immutable';
|
||||
import { TaskManager } from '../mol-plugin/util/task-manager';
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { PluginUIComponent } from './base';
|
||||
import { PluginToastManager } from '../mol-plugin/util/toast';
|
||||
import { IconButton } from './controls/common';
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
import { produce } from 'immer';
|
||||
import * as React from 'react';
|
||||
import { Canvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
|
||||
import { PluginCommands } from '../../mol-plugin/commands';
|
||||
import { StateTransform } from '../../mol-state';
|
||||
|
||||
@@ -267,7 +267,7 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
|
||||
const transform = structure?.models.length === 0 ? void 0 : GlobalModelTransformInfo.get(structure?.models[0]!);
|
||||
await repr.createOrUpdate(props, channel.data).runInContext(ctx);
|
||||
if (transform) repr.setState({ transform });
|
||||
return new SO.Volume.Representation3D({ repr, source: a }, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` });
|
||||
return new SO.Volume.Representation3D({ repr, sourceData: channel.data }, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` });
|
||||
}),
|
||||
update: ({ a, b, newParams, spine }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
|
||||
// TODO : check if params/underlying data/etc have changed; maybe will need to export "data" or some other "tag" in the Representation for this to work
|
||||
@@ -280,6 +280,7 @@ const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
|
||||
const props = { ...b.data.repr.props, ...params.type.params };
|
||||
b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params));
|
||||
await b.data.repr.createOrUpdate(props, channel.data).runInContext(ctx);
|
||||
b.data.sourceData = channel.data;
|
||||
|
||||
// TODO: set the transform here as well in case the structure moves?
|
||||
// doing this here now breaks the code for some reason...
|
||||
|
||||
@@ -54,20 +54,20 @@ export function SyncStructureRepresentation3DState(ctx: PluginContext) {
|
||||
events.object.created.subscribe(e => {
|
||||
if (!SO.Molecule.Structure.Representation3DState.is(e.obj)) return;
|
||||
const data = e.obj.data as SO.Molecule.Structure.Representation3DStateData;
|
||||
data.source.data.repr.setState(data.state);
|
||||
ctx.canvas3d?.update(data.source.data.repr);
|
||||
data.repr.setState(data.state);
|
||||
ctx.canvas3d?.update(data.repr);
|
||||
});
|
||||
events.object.updated.subscribe(e => {
|
||||
if (!SO.Molecule.Structure.Representation3DState.is(e.obj)) return;
|
||||
const data = e.obj.data as SO.Molecule.Structure.Representation3DStateData;
|
||||
data.source.data.repr.setState(data.state);
|
||||
ctx.canvas3d?.update(data.source.data.repr);
|
||||
data.repr.setState(data.state);
|
||||
ctx.canvas3d?.update(data.repr);
|
||||
});
|
||||
events.object.removed.subscribe(e => {
|
||||
if (!SO.Molecule.Structure.Representation3DState.is(e.obj)) return;
|
||||
const data = e.obj.data as SO.Molecule.Structure.Representation3DStateData;
|
||||
data.source.data.repr.setState(data.initialState);
|
||||
ctx.canvas3d?.update(data.source.data.repr);
|
||||
data.repr.setState(data.initialState);
|
||||
ctx.canvas3d?.update(data.repr);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -24,9 +24,6 @@ import { Visual } from './visual';
|
||||
import { CustomProperty } from '../mol-model-props/common/custom-property';
|
||||
import { Clipping } from '../mol-theme/clipping';
|
||||
|
||||
// export interface RepresentationProps {
|
||||
// visuals?: string[]
|
||||
// }
|
||||
export type RepresentationProps = { [k: string]: any }
|
||||
|
||||
export interface RepresentationContext {
|
||||
@@ -54,6 +51,8 @@ export interface RepresentationProvider<D = any, P extends PD.Params = any, S ex
|
||||
attach: (ctx: CustomProperty.Context, data: D) => Promise<void>,
|
||||
detach: (data: D) => void
|
||||
}
|
||||
readonly getData?: (data: D, props: PD.Values<P>) => D
|
||||
readonly mustRecreate?: (oldProps: PD.Values<P>, newProps: PD.Values<P>) => boolean
|
||||
}
|
||||
|
||||
export namespace RepresentationProvider {
|
||||
@@ -66,7 +65,7 @@ export namespace RepresentationProvider {
|
||||
|
||||
export type AnyRepresentationProvider = RepresentationProvider<any, {}, Representation.State>
|
||||
|
||||
export const EmptyRepresentationProvider = {
|
||||
const EmptyRepresentationProvider = {
|
||||
label: '',
|
||||
description: '',
|
||||
factory: () => Representation.Empty,
|
||||
|
||||
@@ -66,7 +66,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
|
||||
}
|
||||
|
||||
function getLoci(pickingId?: PickingId) {
|
||||
if (pickingId === undefined) return Structure.Loci(_structure);
|
||||
if (pickingId === undefined) return Structure.Loci(_structure.target);
|
||||
return visual ? visual.getLoci(pickingId) : EmptyLoci;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ const BallAndStickVisuals = {
|
||||
|
||||
export const BallAndStickParams = {
|
||||
...ElementSphereParams,
|
||||
traceOnly: PD.Boolean(false, { isHidden: true }), // not useful here
|
||||
...IntraUnitBondCylinderParams,
|
||||
...InterUnitBondCylinderParams,
|
||||
unitKinds: getUnitKindsParam(['atomic']),
|
||||
@@ -50,5 +51,11 @@ export const BallAndStickRepresentationProvider = StructureRepresentationProvide
|
||||
defaultValues: PD.getDefaultValues(BallAndStickParams),
|
||||
defaultColorTheme: { name: 'element-symbol' },
|
||||
defaultSizeTheme: { name: 'physical' },
|
||||
isApplicable: (structure: Structure) => structure.elementCount > 0
|
||||
isApplicable: (structure: Structure) => structure.elementCount > 0,
|
||||
getData: (structure: Structure, props: PD.Values<BallAndStickParams>) => {
|
||||
return props.includeParent ? structure.asParent() : structure;
|
||||
},
|
||||
mustRecreate: (oldProps: PD.Values<BallAndStickParams>, newProps: PD.Values<BallAndStickParams>) => {
|
||||
return oldProps.includeParent !== newProps.includeParent;
|
||||
}
|
||||
});
|
||||
@@ -25,6 +25,7 @@ export const EllipsoidParams = {
|
||||
...EllipsoidMeshParams,
|
||||
...IntraUnitBondCylinderParams,
|
||||
...InterUnitBondCylinderParams,
|
||||
adjustCylinderLength: PD.Boolean(false, { isHidden: true }), // not useful here
|
||||
unitKinds: getUnitKindsParam(['atomic']),
|
||||
sizeFactor: PD.Numeric(1, { min: 0.01, max: 10, step: 0.01 }),
|
||||
sizeAspectRatio: PD.Numeric(0.1, { min: 0.01, max: 3, step: 0.01 }),
|
||||
@@ -50,5 +51,11 @@ export const EllipsoidRepresentationProvider = StructureRepresentationProvider({
|
||||
defaultValues: PD.getDefaultValues(EllipsoidParams),
|
||||
defaultColorTheme: { name: 'element-symbol' },
|
||||
defaultSizeTheme: { name: 'uniform' },
|
||||
isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => AtomSiteAnisotrop.Provider.isApplicable(m))
|
||||
isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => AtomSiteAnisotrop.Provider.isApplicable(m)),
|
||||
getData: (structure: Structure, props: PD.Values<EllipsoidParams>) => {
|
||||
return props.includeParent ? structure.asParent() : structure;
|
||||
},
|
||||
mustRecreate: (oldProps: PD.Values<EllipsoidParams>, newProps: PD.Values<EllipsoidParams>) => {
|
||||
return oldProps.includeParent !== newProps.includeParent;
|
||||
}
|
||||
});
|
||||
@@ -46,5 +46,11 @@ export const LineRepresentationProvider = StructureRepresentationProvider({
|
||||
defaultValues: PD.getDefaultValues(LineParams),
|
||||
defaultColorTheme: { name: 'element-symbol' },
|
||||
defaultSizeTheme: { name: 'uniform' },
|
||||
isApplicable: (structure: Structure) => structure.elementCount > 0
|
||||
isApplicable: (structure: Structure) => structure.elementCount > 0,
|
||||
getData: (structure: Structure, props: PD.Values<LineParams>) => {
|
||||
return props.includeParent ? structure.asParent() : structure;
|
||||
},
|
||||
mustRecreate: (oldProps: PD.Values<LineParams>, newProps: PD.Values<LineParams>) => {
|
||||
return oldProps.includeParent !== newProps.includeParent;
|
||||
}
|
||||
});
|
||||
@@ -180,7 +180,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
|
||||
}
|
||||
|
||||
function getLoci(pickingId?: PickingId) {
|
||||
if (pickingId === undefined) return Structure.Loci(_structure);
|
||||
if (pickingId === undefined) return Structure.Loci(_structure.target);
|
||||
let loci: Loci = EmptyLoci;
|
||||
visuals.forEach(({ visual }) => {
|
||||
const _loci = visual.getLoci(pickingId);
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -19,6 +19,7 @@ import { BondCylinderParams, BondIterator, getInterBondLoci, eachInterBond, make
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Cylinders } from '../../../mol-geo/geometry/cylinders/cylinders';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { SortedArray } from '../../../mol-data/int/sorted-array';
|
||||
|
||||
const tmpRefPosBondIt = new Bond.ElementBondIterator();
|
||||
function setRefPosition(pos: Vec3, structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex) {
|
||||
@@ -39,10 +40,33 @@ function getInterUnitBondCylinderBuilderProps(structure: Structure, theme: Theme
|
||||
|
||||
const bonds = structure.interUnitBonds;
|
||||
const { edgeCount, edges } = bonds;
|
||||
const { sizeFactor, sizeAspectRatio } = props;
|
||||
const { sizeFactor, sizeAspectRatio, adjustCylinderLength } = props;
|
||||
|
||||
const delta = Vec3();
|
||||
|
||||
let stub: undefined | ((edgeIndex: number) => boolean);
|
||||
|
||||
if (props.includeParent) {
|
||||
const { child } = structure;
|
||||
if (!child) throw new Error('expected child to exist');
|
||||
|
||||
stub = (edgeIndex: number) => {
|
||||
const b = edges[edgeIndex];
|
||||
const childUnitA = child.unitMap.get(b.unitA);
|
||||
const childUnitB = child.unitMap.get(b.unitB);
|
||||
|
||||
const unitA = structure.unitMap.get(b.unitA);
|
||||
const eA = unitA.elements[b.indexA];
|
||||
const unitB = structure.unitMap.get(b.unitB);
|
||||
const eB = unitB.elements[b.indexB];
|
||||
|
||||
return (
|
||||
childUnitA && SortedArray.has(childUnitA.elements, eA) &&
|
||||
(!childUnitB || !SortedArray.has(childUnitB.elements, eB))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const radius = (edgeIndex: number) => {
|
||||
const b = edges[edgeIndex];
|
||||
locB.aUnit = structure.unitMap.get(b.unitA);
|
||||
@@ -92,19 +116,20 @@ function getInterUnitBondCylinderBuilderProps(structure: Structure, theme: Theme
|
||||
const uA = structure.unitMap.get(b.unitA);
|
||||
const uB = structure.unitMap.get(b.unitB);
|
||||
|
||||
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
|
||||
const r = Math.min(rA, rB) * sizeAspectRatio;
|
||||
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
|
||||
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
|
||||
|
||||
uA.conformation.position(uA.elements[b.indexA], posA);
|
||||
uB.conformation.position(uB.elements[b.indexB], posB);
|
||||
|
||||
if (oA <= 0.01 && oB <= 0.01) return;
|
||||
if (adjustCylinderLength) {
|
||||
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
|
||||
const r = Math.min(rA, rB) * sizeAspectRatio;
|
||||
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
|
||||
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
|
||||
if (oA <= 0.01 && oB <= 0.01) return;
|
||||
|
||||
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
|
||||
Vec3.scaleAndAdd(posA, posA, delta, oA);
|
||||
Vec3.scaleAndAdd(posB, posB, delta, -oB);
|
||||
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
|
||||
Vec3.scaleAndAdd(posA, posA, delta, oA);
|
||||
Vec3.scaleAndAdd(posB, posB, delta, -oB);
|
||||
}
|
||||
},
|
||||
style: (edgeIndex: number) => {
|
||||
const o = edges[edgeIndex].props.order;
|
||||
@@ -123,7 +148,8 @@ function getInterUnitBondCylinderBuilderProps(structure: Structure, theme: Theme
|
||||
radius: (edgeIndex: number) => {
|
||||
return radius(edgeIndex) * sizeAspectRatio;
|
||||
},
|
||||
ignore: makeInterBondIgnoreTest(structure, props)
|
||||
ignore: makeInterBondIgnoreTest(structure, props),
|
||||
stub
|
||||
};
|
||||
}
|
||||
|
||||
@@ -133,7 +159,8 @@ function createInterUnitBondCylinderImpostors(ctx: VisualContext, structure: Str
|
||||
const builderProps = getInterUnitBondCylinderBuilderProps(structure, theme, props);
|
||||
const m = createLinkCylinderImpostors(ctx, builderProps, props, cylinders);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * props.sizeFactor);
|
||||
const { child } = structure;
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * props.sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
|
||||
return m;
|
||||
@@ -145,7 +172,8 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
|
||||
const builderProps = getInterUnitBondCylinderBuilderProps(structure, theme, props);
|
||||
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * props.sizeFactor);
|
||||
const { child } = structure;
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * props.sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
|
||||
return m;
|
||||
@@ -158,6 +186,7 @@ export const InterUnitBondCylinderParams = {
|
||||
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
|
||||
sizeAspectRatio: PD.Numeric(2 / 3, { min: 0, max: 3, step: 0.01 }),
|
||||
tryUseImpostor: PD.Boolean(true),
|
||||
includeParent: PD.Boolean(false),
|
||||
};
|
||||
export type InterUnitBondCylinderParams = typeof InterUnitBondCylinderParams
|
||||
|
||||
@@ -183,8 +212,10 @@ export function InterUnitBondCylinderImpostorVisual(materialId: number): Complex
|
||||
newProps.dashCount !== currentProps.dashCount ||
|
||||
newProps.dashScale !== currentProps.dashScale ||
|
||||
newProps.dashCap !== currentProps.dashCap ||
|
||||
newProps.stubCap !== currentProps.stubCap ||
|
||||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
|
||||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
|
||||
);
|
||||
},
|
||||
mustRecreate: (structure: Structure, props: PD.Values<InterUnitBondCylinderParams>, webgl?: WebGLContext) => {
|
||||
@@ -212,8 +243,10 @@ export function InterUnitBondCylinderMeshVisual(materialId: number): ComplexVisu
|
||||
newProps.dashCount !== currentProps.dashCount ||
|
||||
newProps.dashScale !== currentProps.dashScale ||
|
||||
newProps.dashCap !== currentProps.dashCap ||
|
||||
newProps.stubCap !== currentProps.stubCap ||
|
||||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
|
||||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
|
||||
);
|
||||
},
|
||||
mustRecreate: (structure: Structure, props: PD.Values<InterUnitBondCylinderParams>, webgl?: WebGLContext) => {
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -97,7 +97,8 @@ function createInterUnitBondLines(ctx: VisualContext, structure: Structure, them
|
||||
|
||||
const l = createLinkLines(ctx, builderProps, props, lines);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
|
||||
const { child } = structure;
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (child ?? structure).boundary.sphere, 1 * props.sizeFactor);
|
||||
l.setBoundingSphere(sphere);
|
||||
|
||||
return l;
|
||||
@@ -106,6 +107,7 @@ function createInterUnitBondLines(ctx: VisualContext, structure: Structure, them
|
||||
export const InterUnitBondLineParams = {
|
||||
...ComplexLinesParams,
|
||||
...BondLineParams,
|
||||
includeParent: PD.Boolean(false),
|
||||
};
|
||||
export type InterUnitBondLineParams = typeof InterUnitBondLineParams
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -21,23 +21,39 @@ import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { IntAdjacencyGraph } from '../../../mol-math/graph';
|
||||
import { WebGLContext } from '../../../mol-gl/webgl/context';
|
||||
import { Cylinders } from '../../../mol-geo/geometry/cylinders/cylinders';
|
||||
import { SortedArray } from '../../../mol-data/int';
|
||||
|
||||
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
||||
const isBondType = BondType.is;
|
||||
|
||||
function getIntraUnitBondCylinderBuilderProps(unit: Unit.Atomic, structure: Structure, theme: Theme, props: PD.Values<IntraUnitBondCylinderParams>) {
|
||||
const locE = StructureElement.Location.create(structure, unit);
|
||||
const locB = Bond.Location(structure, unit, undefined, structure, unit, undefined);
|
||||
|
||||
const elements = unit.elements;
|
||||
const bonds = unit.bonds;
|
||||
const { edgeCount, a, b, edgeProps, offset } = bonds;
|
||||
const { order: _order, flags: _flags } = edgeProps;
|
||||
const { sizeFactor, sizeAspectRatio } = props;
|
||||
const { sizeFactor, sizeAspectRatio, adjustCylinderLength } = props;
|
||||
|
||||
const vRef = Vec3(), delta = Vec3();
|
||||
const pos = unit.conformation.invariantPosition;
|
||||
|
||||
let stub: undefined | ((edgeIndex: number) => boolean);
|
||||
|
||||
const locE = StructureElement.Location.create(structure, unit);
|
||||
const locB = Bond.Location(structure, unit, undefined, structure, unit, undefined);
|
||||
|
||||
if (props.includeParent) {
|
||||
const { child } = structure;
|
||||
if (!child) throw new Error('expected child to exist');
|
||||
const childUnit = child.unitMap.get(unit.id);
|
||||
if (!childUnit) throw new Error('expected childUnit to exist');
|
||||
|
||||
stub = (edgeIndex: number) => {
|
||||
const eA = elements[a[edgeIndex]];
|
||||
const eB = elements[b[edgeIndex]];
|
||||
return SortedArray.has(childUnit.elements, eA) && !SortedArray.has(childUnit.elements, eB);
|
||||
};
|
||||
}
|
||||
|
||||
const radius = (edgeIndex: number) => {
|
||||
locB.aIndex = a[edgeIndex];
|
||||
locB.bIndex = b[edgeIndex];
|
||||
@@ -74,19 +90,20 @@ function getIntraUnitBondCylinderBuilderProps(unit: Unit.Atomic, structure: Stru
|
||||
return null;
|
||||
},
|
||||
position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
|
||||
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
|
||||
const r = Math.min(rA, rB) * sizeAspectRatio;
|
||||
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
|
||||
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
|
||||
|
||||
pos(elements[a[edgeIndex]], posA);
|
||||
pos(elements[b[edgeIndex]], posB);
|
||||
|
||||
if (oA <= 0.01 && oB <= 0.01) return;
|
||||
if (adjustCylinderLength) {
|
||||
const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
|
||||
const r = Math.min(rA, rB) * sizeAspectRatio;
|
||||
const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
|
||||
const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
|
||||
if (oA <= 0.01 && oB <= 0.01) return;
|
||||
|
||||
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
|
||||
Vec3.scaleAndAdd(posA, posA, delta, oA);
|
||||
Vec3.scaleAndAdd(posB, posB, delta, -oB);
|
||||
Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
|
||||
Vec3.scaleAndAdd(posA, posA, delta, oA);
|
||||
Vec3.scaleAndAdd(posB, posB, delta, -oB);
|
||||
}
|
||||
},
|
||||
style: (edgeIndex: number) => {
|
||||
const o = _order[edgeIndex];
|
||||
@@ -105,7 +122,8 @@ function getIntraUnitBondCylinderBuilderProps(unit: Unit.Atomic, structure: Stru
|
||||
radius: (edgeIndex: number) => {
|
||||
return radius(edgeIndex) * sizeAspectRatio;
|
||||
},
|
||||
ignore: makeIntraBondIgnoreTest(unit, props)
|
||||
ignore: makeIntraBondIgnoreTest(structure, unit, props),
|
||||
stub
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,10 +131,14 @@ function createIntraUnitBondCylinderImpostors(ctx: VisualContext, unit: Unit, st
|
||||
if (!Unit.isAtomic(unit)) return Cylinders.createEmpty(cylinders);
|
||||
if (!unit.bonds.edgeCount) return Cylinders.createEmpty(cylinders);
|
||||
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) return Cylinders.createEmpty(cylinders);
|
||||
|
||||
const builderProps = getIntraUnitBondCylinderBuilderProps(unit, structure, theme, props);
|
||||
const c = createLinkCylinderImpostors(ctx, builderProps, props, cylinders);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * props.sizeFactor);
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * props.sizeFactor);
|
||||
c.setBoundingSphere(sphere);
|
||||
|
||||
return c;
|
||||
@@ -126,10 +148,14 @@ function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structu
|
||||
if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
|
||||
if (!unit.bonds.edgeCount) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) return Mesh.createEmpty(mesh);
|
||||
|
||||
const builderProps = getIntraUnitBondCylinderBuilderProps(unit, structure, theme, props);
|
||||
const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * props.sizeFactor);
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * props.sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
|
||||
return m;
|
||||
@@ -142,6 +168,7 @@ export const IntraUnitBondCylinderParams = {
|
||||
sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
|
||||
sizeAspectRatio: PD.Numeric(2 / 3, { min: 0, max: 3, step: 0.01 }),
|
||||
tryUseImpostor: PD.Boolean(true),
|
||||
includeParent: PD.Boolean(false),
|
||||
};
|
||||
export type IntraUnitBondCylinderParams = typeof IntraUnitBondCylinderParams
|
||||
|
||||
@@ -167,8 +194,10 @@ export function IntraUnitBondCylinderImpostorVisual(materialId: number): UnitsVi
|
||||
newProps.dashCount !== currentProps.dashCount ||
|
||||
newProps.dashScale !== currentProps.dashScale ||
|
||||
newProps.dashCap !== currentProps.dashCap ||
|
||||
newProps.stubCap !== currentProps.stubCap ||
|
||||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
|
||||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
|
||||
);
|
||||
|
||||
const newUnit = newStructureGroup.group.units[0];
|
||||
@@ -207,8 +236,10 @@ export function IntraUnitBondCylinderMeshVisual(materialId: number): UnitsVisual
|
||||
newProps.dashCount !== currentProps.dashCount ||
|
||||
newProps.dashScale !== currentProps.dashScale ||
|
||||
newProps.dashCap !== currentProps.dashCap ||
|
||||
newProps.stubCap !== currentProps.stubCap ||
|
||||
!arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
|
||||
!arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ||
|
||||
newProps.adjustCylinderLength !== currentProps.adjustCylinderLength
|
||||
);
|
||||
|
||||
const newUnit = newStructureGroup.group.units[0];
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -25,6 +25,14 @@ const isBondType = BondType.is;
|
||||
function createIntraUnitBondLines(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitBondLineParams>, lines?: Lines) {
|
||||
if (!Unit.isAtomic(unit)) return Lines.createEmpty(lines);
|
||||
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) return Lines.createEmpty(lines);
|
||||
|
||||
if (props.includeParent) {
|
||||
if (!child) throw new Error('expected child to exist');
|
||||
}
|
||||
|
||||
const location = StructureElement.Location.create(structure, unit);
|
||||
|
||||
const elements = unit.elements;
|
||||
@@ -82,12 +90,12 @@ function createIntraUnitBondLines(ctx: VisualContext, unit: Unit, structure: Str
|
||||
const sizeB = theme.size.size(location);
|
||||
return Math.min(sizeA, sizeB) * sizeFactor;
|
||||
},
|
||||
ignore: makeIntraBondIgnoreTest(unit, props)
|
||||
ignore: makeIntraBondIgnoreTest(structure, unit, props)
|
||||
};
|
||||
|
||||
const l = createLinkLines(ctx, builderProps, props, lines);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * sizeFactor);
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * sizeFactor);
|
||||
l.setBoundingSphere(sphere);
|
||||
|
||||
return l;
|
||||
@@ -96,6 +104,7 @@ function createIntraUnitBondLines(ctx: VisualContext, unit: Unit, structure: Str
|
||||
export const IntraUnitBondLineParams = {
|
||||
...UnitsLinesParams,
|
||||
...BondLineParams,
|
||||
includeParent: PD.Boolean(false),
|
||||
};
|
||||
export type IntraUnitBondLineParams = typeof IntraUnitBondLineParams
|
||||
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -29,17 +29,20 @@ export type ElementPointParams = typeof ElementPointParams
|
||||
export function createElementPoint(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ElementPointParams>, points: Points) {
|
||||
// TODO sizeFactor
|
||||
|
||||
const { child } = structure;
|
||||
if (child && !child.unitMap.get(unit.id)) return Points.createEmpty(points);
|
||||
|
||||
const elements = unit.elements;
|
||||
const n = elements.length;
|
||||
const builder = PointsBuilder.create(n, n / 10, points);
|
||||
|
||||
const p = Vec3();
|
||||
const pos = unit.conformation.invariantPosition;
|
||||
const ignore = makeElementIgnoreTest(unit, props);
|
||||
const ignore = makeElementIgnoreTest(structure, unit, props);
|
||||
|
||||
if (ignore) {
|
||||
for (let i = 0; i < n; ++i) {
|
||||
if (ignore(unit, elements[i])) continue;
|
||||
if (ignore(elements[i])) continue;
|
||||
pos(elements[i], p);
|
||||
builder.add(p[0], p[1], p[2], i);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -22,6 +22,7 @@ import { equalEps } from '../../../mol-math/linear-algebra/3d/common';
|
||||
import { addSphere } from '../../../mol-geo/geometry/mesh/builder/sphere';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { BaseGeometry } from '../../../mol-geo/geometry/base';
|
||||
import { SortedArray } from '../../../mol-data/int/sorted-array';
|
||||
|
||||
export const EllipsoidMeshParams = {
|
||||
...UnitsMeshParams,
|
||||
@@ -57,7 +58,11 @@ export interface EllipsoidMeshProps {
|
||||
}
|
||||
|
||||
export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: EllipsoidMeshProps, mesh?: Mesh): Mesh {
|
||||
const { detail, sizeFactor } = props;
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { detail, sizeFactor, ignoreHydrogens } = props;
|
||||
|
||||
const { elements, model } = unit;
|
||||
const { atomicNumber } = unit.model.atomicHierarchy.derived.atom;
|
||||
@@ -84,7 +89,8 @@ export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: S
|
||||
const ei = elements[i];
|
||||
const ai = elementToAnsiotrop[ei];
|
||||
if (ai === -1) continue;
|
||||
if (props.ignoreHydrogens && isH(atomicNumber, ei)) continue;
|
||||
if (((!!childUnit && !SortedArray.has(childUnit.elements, ei))) ||
|
||||
(ignoreHydrogens && isH(atomicNumber, ei))) continue;
|
||||
|
||||
l.element = ei;
|
||||
pos(ei, v);
|
||||
@@ -111,7 +117,7 @@ export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: S
|
||||
|
||||
const m = MeshBuilder.getMesh(builderState);
|
||||
|
||||
const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * sizeFactor);
|
||||
const sphere = Sphere3D.expand(Sphere3D(), (childUnit || unit).boundary.sphere, 1 * sizeFactor);
|
||||
m.setBoundingSphere(sphere);
|
||||
|
||||
return m;
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -13,7 +13,7 @@ import { LinkCylinderParams, LinkLineParams } from './link';
|
||||
import { ObjectKeys } from '../../../../mol-util/type-helpers';
|
||||
import { PickingId } from '../../../../mol-geo/geometry/picking';
|
||||
import { EmptyLoci, Loci } from '../../../../mol-model/loci';
|
||||
import { Interval, OrderedSet } from '../../../../mol-data/int';
|
||||
import { Interval, OrderedSet, SortedArray } from '../../../../mol-data/int';
|
||||
import { isH, isHydrogen } from './common';
|
||||
|
||||
export const BondParams = {
|
||||
@@ -26,7 +26,8 @@ export type BondProps = typeof DefaultBondProps
|
||||
|
||||
export const BondCylinderParams = {
|
||||
...LinkCylinderParams,
|
||||
...BondParams
|
||||
...BondParams,
|
||||
adjustCylinderLength: PD.Boolean(true, { description: 'Shorten cylinders to reduce overlap with spheres.' })
|
||||
};
|
||||
export const DefaultBondCylinderProps = PD.getDefaultValues(BondCylinderParams);
|
||||
export type BondCylinderProps = typeof DefaultBondCylinderProps
|
||||
@@ -42,7 +43,7 @@ export function ignoreBondType(include: BondType.Flag, exclude: BondType.Flag, f
|
||||
return !BondType.is(include, f) || BondType.is(exclude, f);
|
||||
}
|
||||
|
||||
export function makeIntraBondIgnoreTest(unit: Unit.Atomic, props: BondProps): undefined | ((edgeIndex: number) => boolean) {
|
||||
export function makeIntraBondIgnoreTest(structure: Structure, unit: Unit.Atomic, props: BondProps): undefined | ((edgeIndex: number) => boolean) {
|
||||
const elements = unit.elements;
|
||||
const { atomicNumber } = unit.model.atomicHierarchy.derived.atom;
|
||||
const bonds = unit.bonds;
|
||||
@@ -53,16 +54,21 @@ export function makeIntraBondIgnoreTest(unit: Unit.Atomic, props: BondProps): un
|
||||
|
||||
const include = BondType.fromNames(includeTypes);
|
||||
const exclude = BondType.fromNames(excludeTypes);
|
||||
|
||||
const allBondTypes = BondType.isAll(include) && BondType.Flag.None === exclude;
|
||||
|
||||
if (!allBondTypes && ignoreHydrogens) {
|
||||
return (edgeIndex: number) => isH(atomicNumber, elements[a[edgeIndex]]) || isH(atomicNumber, elements[b[edgeIndex]]) || ignoreBondType(include, exclude, _flags[edgeIndex]);
|
||||
} else if (!allBondTypes) {
|
||||
return (edgeIndex: number) => ignoreBondType(include, exclude, _flags[edgeIndex]);
|
||||
} else if (ignoreHydrogens) {
|
||||
return (edgeIndex: number) => isH(atomicNumber, elements[a[edgeIndex]]) || isH(atomicNumber, elements[b[edgeIndex]]);
|
||||
}
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) throw new Error('expected childUnit to exist if child exists');
|
||||
|
||||
if (allBondTypes && !ignoreHydrogens && !child) return;
|
||||
|
||||
return (edgeIndex: number) => {
|
||||
return (
|
||||
(!!childUnit && !SortedArray.has(childUnit.elements, elements[a[edgeIndex]])) ||
|
||||
(ignoreHydrogens && (isH(atomicNumber, elements[a[edgeIndex]]) || isH(atomicNumber, elements[b[edgeIndex]]))) ||
|
||||
(!allBondTypes && ignoreBondType(include, exclude, _flags[edgeIndex]))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function makeInterBondIgnoreTest(structure: Structure, props: BondProps): undefined | ((edgeIndex: number) => boolean) {
|
||||
@@ -73,34 +79,45 @@ export function makeInterBondIgnoreTest(structure: Structure, props: BondProps):
|
||||
|
||||
const include = BondType.fromNames(includeTypes);
|
||||
const exclude = BondType.fromNames(excludeTypes);
|
||||
|
||||
const allBondTypes = BondType.isAll(include) && BondType.Flag.None === exclude;
|
||||
|
||||
const ignoreHydrogen = (edgeIndex: number) => {
|
||||
const b = edges[edgeIndex];
|
||||
const uA = structure.unitMap.get(b.unitA);
|
||||
const uB = structure.unitMap.get(b.unitB);
|
||||
return isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB]);
|
||||
};
|
||||
const { child } = structure;
|
||||
|
||||
if (!allBondTypes && ignoreHydrogens) {
|
||||
return (edgeIndex: number) => ignoreHydrogen(edgeIndex) || ignoreBondType(include, exclude, edges[edgeIndex].props.flag);
|
||||
} else if (!allBondTypes) {
|
||||
return (edgeIndex: number) => ignoreBondType(include, exclude, edges[edgeIndex].props.flag);
|
||||
} else if (ignoreHydrogens) {
|
||||
return (edgeIndex: number) => ignoreHydrogen(edgeIndex);
|
||||
}
|
||||
if (allBondTypes && !ignoreHydrogens && !child) return;
|
||||
|
||||
return (edgeIndex: number) => {
|
||||
if (child) {
|
||||
const b = edges[edgeIndex];
|
||||
const childUnitA = child.unitMap.get(b.unitA);
|
||||
if (!childUnitA) return true;
|
||||
|
||||
const unitA = structure.unitMap.get(b.unitA);
|
||||
const eA = unitA.elements[b.indexA];
|
||||
if (!SortedArray.has(childUnitA.elements, eA)) return true;
|
||||
}
|
||||
|
||||
if (ignoreHydrogens) {
|
||||
const b = edges[edgeIndex];
|
||||
const uA = structure.unitMap.get(b.unitA);
|
||||
const uB = structure.unitMap.get(b.unitB);
|
||||
if(isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB])) return true;
|
||||
}
|
||||
|
||||
if (!allBondTypes) {
|
||||
if (ignoreBondType(include, exclude, edges[edgeIndex].props.flag)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace BondIterator {
|
||||
export function fromGroup(structureGroup: StructureGroup): LocationIterator {
|
||||
const { group, structure } = structureGroup;
|
||||
const unit = group.units[0];
|
||||
const unit = group.units[0] as Unit.Atomic;
|
||||
const groupCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0;
|
||||
const instanceCount = group.units.length;
|
||||
const location = Bond.Location();
|
||||
location.aStructure = structure;
|
||||
location.bStructure = structure;
|
||||
const location = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
|
||||
const getLocation = (groupIndex: number, instanceIndex: number) => {
|
||||
const unit = group.units[instanceIndex] as Unit.Atomic;
|
||||
location.aUnit = unit;
|
||||
@@ -115,9 +132,7 @@ export namespace BondIterator {
|
||||
export function fromStructure(structure: Structure): LocationIterator {
|
||||
const groupCount = structure.interUnitBonds.edgeCount;
|
||||
const instanceCount = 1;
|
||||
const location = Bond.Location();
|
||||
location.aStructure = structure;
|
||||
location.bStructure = structure;
|
||||
const location = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
|
||||
const getLocation = (groupIndex: number) => {
|
||||
const bond = structure.interUnitBonds.edges[groupIndex];
|
||||
location.aUnit = structure.unitMap.get(bond.unitA);
|
||||
@@ -138,15 +153,12 @@ export function getIntraBondLoci(pickingId: PickingId, structureGroup: Structure
|
||||
const { structure, group } = structureGroup;
|
||||
const unit = group.units[instanceId];
|
||||
if (Unit.isAtomic(unit)) {
|
||||
return Bond.Loci(structure, [
|
||||
Bond.Location(
|
||||
structure, unit, unit.bonds.a[groupId] as StructureElement.UnitIndex,
|
||||
structure, unit, unit.bonds.b[groupId] as StructureElement.UnitIndex
|
||||
),
|
||||
Bond.Location(
|
||||
structure, unit, unit.bonds.b[groupId] as StructureElement.UnitIndex,
|
||||
structure, unit, unit.bonds.a[groupId] as StructureElement.UnitIndex
|
||||
)
|
||||
const { target } = structure;
|
||||
const iA = unit.bonds.a[groupId];
|
||||
const iB = unit.bonds.b[groupId];
|
||||
return Bond.Loci(target, [
|
||||
Bond.Location(target, unit, iA, target, unit, iB),
|
||||
Bond.Location(target, unit, iB, target, unit, iA)
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -199,12 +211,13 @@ export function eachIntraBond(loci: Loci, structureGroup: StructureGroup, apply:
|
||||
export function getInterBondLoci(pickingId: PickingId, structure: Structure, id: number) {
|
||||
const { objectId, groupId } = pickingId;
|
||||
if (id === objectId) {
|
||||
const { target } = structure;
|
||||
const b = structure.interUnitBonds.edges[groupId];
|
||||
const uA = structure.unitMap.get(b.unitA);
|
||||
const uB = structure.unitMap.get(b.unitB);
|
||||
return Bond.Loci(structure, [
|
||||
Bond.Location(structure, uA, b.indexA, structure, uB, b.indexB),
|
||||
Bond.Location(structure, uB, b.indexB, structure, uA, b.indexA)
|
||||
return Bond.Loci(target, [
|
||||
Bond.Location(target, uA, b.indexA, target, uB, b.indexB),
|
||||
Bond.Location(target, uB, b.indexB, target, uA, b.indexA)
|
||||
]);
|
||||
}
|
||||
return EmptyLoci;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { Unit, StructureElement, Structure, ElementIndex } from '../../../../mol-model/structure';
|
||||
import { Loci, EmptyLoci } from '../../../../mol-model/loci';
|
||||
import { Interval, OrderedSet } from '../../../../mol-data/int';
|
||||
import { Interval, OrderedSet, SortedArray } from '../../../../mol-data/int';
|
||||
import { Mesh } from '../../../../mol-geo/geometry/mesh/mesh';
|
||||
import { sphereVertexCount } from '../../../../mol-geo/primitive/sphere';
|
||||
import { MeshBuilder } from '../../../../mol-geo/geometry/mesh/mesh-builder';
|
||||
@@ -36,22 +36,32 @@ export type ElementSphereMeshProps = {
|
||||
sizeFactor: number,
|
||||
} & ElementProps
|
||||
|
||||
export function makeElementIgnoreTest(unit: Unit, props: ElementProps): undefined | ((unit: Unit, i: ElementIndex) => boolean) {
|
||||
export function makeElementIgnoreTest(structure: Structure, unit: Unit, props: ElementProps): undefined | ((i: ElementIndex) => boolean) {
|
||||
const { ignoreHydrogens, traceOnly } = props;
|
||||
|
||||
const { atomicNumber } = unit.model.atomicHierarchy.derived.atom;
|
||||
const isCoarse = Unit.isCoarse(unit);
|
||||
|
||||
if (!isCoarse && ignoreHydrogens && traceOnly) {
|
||||
return (unit: Unit, element: ElementIndex) => isH(atomicNumber, element) && !isTrace(unit, element);
|
||||
} else if (!isCoarse && ignoreHydrogens) {
|
||||
return (unit: Unit, element: ElementIndex) => isH(atomicNumber, element);
|
||||
} else if (!isCoarse && traceOnly) {
|
||||
return (unit: Unit, element: ElementIndex) => !isTrace(unit, element);
|
||||
}
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) throw new Error('expected childUnit to exist if child exists');
|
||||
|
||||
if (!child && ((!ignoreHydrogens && !traceOnly) || traceOnly)) return;
|
||||
|
||||
return (element: ElementIndex) => {
|
||||
return (
|
||||
(!!childUnit && !SortedArray.has(childUnit.elements, element)) ||
|
||||
(!isCoarse && ignoreHydrogens && isH(atomicNumber, element)) ||
|
||||
(traceOnly && !isTrace(unit, element))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereMeshProps, mesh?: Mesh): Mesh {
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { detail, sizeFactor } = props;
|
||||
|
||||
const { elements } = unit;
|
||||
@@ -61,7 +71,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
|
||||
|
||||
const v = Vec3();
|
||||
const pos = unit.conformation.invariantPosition;
|
||||
const ignore = makeElementIgnoreTest(unit, props);
|
||||
const ignore = makeElementIgnoreTest(structure, unit, props);
|
||||
const l = StructureElement.Location.create(structure, unit);
|
||||
const themeSize = theme.size.size;
|
||||
const center = Vec3();
|
||||
@@ -69,7 +79,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < elementCount; i++) {
|
||||
if (ignore && ignore(unit, elements[i])) continue;
|
||||
if (ignore && ignore(elements[i])) continue;
|
||||
|
||||
l.element = elements[i];
|
||||
pos(elements[i], v);
|
||||
@@ -89,7 +99,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
|
||||
if (mesh && Vec3.distance(center, mesh.boundingSphere.center) / mesh.boundingSphere.radius < 1.0) {
|
||||
boundingSphere = Sphere3D.clone(mesh.boundingSphere);
|
||||
} else {
|
||||
boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, maxSize * sizeFactor + 0.05);
|
||||
boundingSphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, maxSize * sizeFactor + 0.05);
|
||||
}
|
||||
|
||||
const m = MeshBuilder.getMesh(builderState);
|
||||
@@ -103,13 +113,17 @@ export type ElementSphereImpostorProps = {
|
||||
} & ElementProps
|
||||
|
||||
export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: ElementSphereImpostorProps, spheres?: Spheres): Spheres {
|
||||
const { child } = structure;
|
||||
const childUnit = child?.unitMap.get(unit.id);
|
||||
if (child && !childUnit) return Spheres.createEmpty(spheres);
|
||||
|
||||
const { elements } = unit;
|
||||
const elementCount = elements.length;
|
||||
const builder = SpheresBuilder.create(elementCount, elementCount / 2, spheres);
|
||||
|
||||
const v = Vec3();
|
||||
const pos = unit.conformation.invariantPosition;
|
||||
const ignore = makeElementIgnoreTest(unit, props);
|
||||
const ignore = makeElementIgnoreTest(structure, unit, props);
|
||||
|
||||
const l = StructureElement.Location.create(structure, unit);
|
||||
const themeSize = theme.size.size;
|
||||
@@ -118,7 +132,7 @@ export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, stru
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < elementCount; i++) {
|
||||
if (ignore?.(unit, elements[i])) continue;
|
||||
if (ignore?.(elements[i])) continue;
|
||||
|
||||
pos(elements[i], v);
|
||||
builder.add(v[0], v[1], v[2], i);
|
||||
@@ -136,7 +150,7 @@ export function createElementSphereImpostor(ctx: VisualContext, unit: Unit, stru
|
||||
if (spheres && Vec3.distance(center, spheres.boundingSphere.center) / spheres.boundingSphere.radius < 1.0) {
|
||||
boundingSphere = Sphere3D.clone(spheres.boundingSphere);
|
||||
} else {
|
||||
boundingSphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, maxSize * props.sizeFactor + 0.05);
|
||||
boundingSphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, maxSize * props.sizeFactor + 0.05);
|
||||
}
|
||||
|
||||
const s = builder.getSpheres();
|
||||
@@ -180,7 +194,7 @@ export function getElementLoci(pickingId: PickingId, structureGroup: StructureGr
|
||||
const { structure, group } = structureGroup;
|
||||
const unit = group.units[instanceId];
|
||||
const indices = OrderedSet.ofSingleton(groupId as StructureElement.UnitIndex);
|
||||
return StructureElement.Loci(structure, [{ unit, indices }]);
|
||||
return StructureElement.Loci(structure.target, [{ unit, indices }]);
|
||||
}
|
||||
return EmptyLoci;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@@ -24,6 +24,7 @@ export const LinkCylinderParams = {
|
||||
dashCount: PD.Numeric(4, { min: 2, max: 10, step: 2 }),
|
||||
dashScale: PD.Numeric(0.8, { min: 0, max: 2, step: 0.1 }),
|
||||
dashCap: PD.Boolean(true),
|
||||
stubCap: PD.Boolean(true),
|
||||
radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }, BaseGeometry.CustomQualityParamInfo),
|
||||
};
|
||||
export const DefaultLinkCylinderProps = PD.getDefaultValues(LinkCylinderParams);
|
||||
@@ -75,6 +76,7 @@ export interface LinkBuilderProps {
|
||||
referencePosition?: (edgeIndex: number) => Vec3 | null
|
||||
style?: (edgeIndex: number) => LinkStyle
|
||||
ignore?: (edgeIndex: number) => boolean
|
||||
stub?: (edgeIndex: number) => boolean
|
||||
}
|
||||
|
||||
export const enum LinkStyle {
|
||||
@@ -97,11 +99,11 @@ const v3dot = Vec3.dot;
|
||||
* the half closer to the first vertex, i.e. vertex a.
|
||||
*/
|
||||
export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuilderProps, props: LinkCylinderProps, mesh?: Mesh) {
|
||||
const { linkCount, referencePosition, position, style, radius, ignore } = linkBuilder;
|
||||
const { linkCount, referencePosition, position, style, radius, ignore, stub } = linkBuilder;
|
||||
|
||||
if (!linkCount) return Mesh.createEmpty(mesh);
|
||||
|
||||
const { linkScale, linkSpacing, radialSegments, linkCap, dashCount, dashScale, dashCap } = props;
|
||||
const { linkScale, linkSpacing, radialSegments, linkCap, dashCount, dashScale, dashCap, stubCap } = props;
|
||||
|
||||
const vertexCountEstimate = radialSegments * 2 * linkCount * 2;
|
||||
const builderState = MeshBuilder.createState(vertexCountEstimate, vertexCountEstimate / 4, mesh);
|
||||
@@ -127,7 +129,8 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
|
||||
|
||||
const linkRadius = radius(edgeIndex);
|
||||
const linkStyle = style ? style(edgeIndex) : LinkStyle.Solid;
|
||||
const [topCap, bottomCap] = (v3dot(tmpV12, up) > 0) ? [false, linkCap] : [linkCap, false];
|
||||
const linkStub = stubCap && (stub ? stub(edgeIndex) : false);
|
||||
const [topCap, bottomCap] = (v3dot(tmpV12, up) > 0) ? [linkStub, linkCap] : [linkCap, linkStub];
|
||||
builderState.currentGroup = edgeIndex;
|
||||
|
||||
if (linkStyle === LinkStyle.Solid) {
|
||||
@@ -176,11 +179,11 @@ export function createLinkCylinderMesh(ctx: VisualContext, linkBuilder: LinkBuil
|
||||
* the half closer to the first vertex, i.e. vertex a.
|
||||
*/
|
||||
export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: LinkBuilderProps, props: LinkCylinderProps, cylinders?: Cylinders) {
|
||||
const { linkCount, referencePosition, position, style, radius, ignore } = linkBuilder;
|
||||
const { linkCount, referencePosition, position, style, radius, ignore, stub } = linkBuilder;
|
||||
|
||||
if (!linkCount) return Cylinders.createEmpty(cylinders);
|
||||
|
||||
const { linkScale, linkSpacing, linkCap, dashCount, dashScale, dashCap } = props;
|
||||
const { linkScale, linkSpacing, linkCap, dashCount, dashScale, dashCap, stubCap } = props;
|
||||
|
||||
const cylindersCountEstimate = linkCount * 2;
|
||||
const builder = CylindersBuilder.create(cylindersCountEstimate, cylindersCountEstimate / 4, cylinders);
|
||||
@@ -200,10 +203,11 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
|
||||
|
||||
const linkRadius = radius(edgeIndex);
|
||||
const linkStyle = style ? style(edgeIndex) : LinkStyle.Solid;
|
||||
const linkStub = stubCap && (stub ? stub(edgeIndex) : false);
|
||||
|
||||
if (linkStyle === LinkStyle.Solid) {
|
||||
v3scale(vb, v3add(vb, va, vb), 0.5);
|
||||
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, false, edgeIndex);
|
||||
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, linkStub, edgeIndex);
|
||||
} else if (linkStyle === LinkStyle.Dashed) {
|
||||
v3scale(tmpV12, v3sub(tmpV12, vb, va), lengthScale);
|
||||
v3sub(vb, vb, tmpV12);
|
||||
@@ -218,14 +222,14 @@ export function createLinkCylinderImpostors(ctx: VisualContext, linkBuilder: Lin
|
||||
v3setMagnitude(vShift, vShift, absOffset);
|
||||
|
||||
if (order === 3) builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], multiScale, linkCap, false, edgeIndex);
|
||||
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vb[0] + vShift[0], vb[1] + vShift[1], vb[2] + vShift[2], multiScale, linkCap, false, edgeIndex);
|
||||
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vb[0] - vShift[0], vb[1] - vShift[1], vb[2] - vShift[2], multiScale, linkCap, false, edgeIndex);
|
||||
builder.add(va[0] + vShift[0], va[1] + vShift[1], va[2] + vShift[2], vb[0] + vShift[0], vb[1] + vShift[1], vb[2] + vShift[2], multiScale, linkCap, linkStub, edgeIndex);
|
||||
builder.add(va[0] - vShift[0], va[1] - vShift[1], va[2] - vShift[2], vb[0] - vShift[0], vb[1] - vShift[1], vb[2] - vShift[2], multiScale, linkCap, linkStub, edgeIndex);
|
||||
} else if (linkStyle === LinkStyle.Disk) {
|
||||
v3scale(tmpV12, v3sub(tmpV12, vb, va), 0.475);
|
||||
v3add(va, va, tmpV12);
|
||||
v3sub(vb, vb, tmpV12);
|
||||
|
||||
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, false, edgeIndex);
|
||||
builder.add(va[0], va[1], va[2], vb[0], vb[1], vb[2], 1, linkCap, linkStub, edgeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"noEmitHelpers": true,
|
||||
"jsx": "react",
|
||||
"jsx": "react-jsx",
|
||||
"lib": [ "es6", "dom", "esnext.asynciterable", "es2016" ],
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/commonjs"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"noEmitHelpers": true,
|
||||
"jsx": "react",
|
||||
"jsx": "react-jsx",
|
||||
"lib": [ "es6", "dom", "esnext.asynciterable", "es2016" ],
|
||||
"rootDir": "src",
|
||||
"outDir": "lib"
|
||||
|
||||
Reference in New Issue
Block a user