mirror of
https://github.com/molstar/molstar.git
synced 2026-06-07 07:04:22 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0b54e9cbf | ||
|
|
fdb45c3624 | ||
|
|
f3b1ec0ed8 | ||
|
|
79510614b9 | ||
|
|
f8c9bc6812 | ||
|
|
85d35aab90 | ||
|
|
f5b09dbd10 | ||
|
|
57ea322fd6 | ||
|
|
c31aab5594 | ||
|
|
a5556e8c41 | ||
|
|
0ce2966c47 | ||
|
|
f2c04a13af | ||
|
|
60ba0de219 | ||
|
|
99e515604e | ||
|
|
84dfa60fc2 | ||
|
|
a753095f92 | ||
|
|
ce0ff2fed8 |
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -24,6 +24,7 @@
|
||||
"serve": "http-server -p 1338",
|
||||
"model-server": "node lib/servers/model/server.js",
|
||||
"model-server-watch": "nodemon --watch lib lib/servers/model/server.js",
|
||||
"volume-server": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"preversion": "npm run test",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
|
||||
@@ -8,7 +8,7 @@ import { StateAction } from '../../../../mol-state';
|
||||
import { PluginContext } from '../../../../mol-plugin/context';
|
||||
import { PluginStateObject as PSO } from '../../../../mol-plugin/state/objects';
|
||||
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
import { Ingredient, CellPacking } from './data';
|
||||
import { Ingredient, CellPacking, Cell } from './data';
|
||||
import { getFromPdb, getFromCellPackDB } from './util';
|
||||
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext } from '../../../../mol-model/structure';
|
||||
import { trajectoryFromMmCIF } from '../../../../mol-model-formats/structure/mmcif';
|
||||
@@ -28,6 +28,11 @@ import { compile } from '../../../../mol-script/runtime/query/compiler';
|
||||
import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
|
||||
import { ThemeRegistryContext } from '../../../../mol-theme/theme';
|
||||
import { ColorTheme } from '../../../../mol-theme/color';
|
||||
import { _parse_mmCif } from '../../../../mol-model-formats/structure/mmcif/parser';
|
||||
import { ModelFormat } from '../../../../mol-model-formats/structure/format';
|
||||
import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Column } from '../../../../mol-data/db';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`
|
||||
@@ -118,6 +123,109 @@ function getAssembly(transforms: Mat4[], structure: Structure) {
|
||||
return builder.getStructure();
|
||||
}
|
||||
|
||||
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const d = model.sourceData.data.atom_site
|
||||
const n = d._rowCount
|
||||
const rowCount = n * transforms.length
|
||||
|
||||
const { offsets, count } = model.atomicHierarchy.chainAtomSegments
|
||||
|
||||
const x = d.Cartn_x.toArray()
|
||||
const y = d.Cartn_y.toArray()
|
||||
const z = d.Cartn_z.toArray()
|
||||
|
||||
const Cartn_x = new Float32Array(rowCount)
|
||||
const Cartn_y = new Float32Array(rowCount)
|
||||
const Cartn_z = new Float32Array(rowCount)
|
||||
const map = new Uint32Array(rowCount)
|
||||
const seq = new Int32Array(rowCount)
|
||||
let offset = 0
|
||||
for (let c = 0; c < count; ++c) {
|
||||
const cStart = offsets[c]
|
||||
const cEnd = offsets[c + 1]
|
||||
const cLength = cEnd - cStart
|
||||
for (let t = 0, tl = transforms.length; t < tl; ++t) {
|
||||
const m = transforms[t]
|
||||
for (let j = cStart; j < cEnd; ++j) {
|
||||
const i = offset + j - cStart
|
||||
const xj = x[j], yj = y[j], zj = z[j]
|
||||
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12]
|
||||
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13]
|
||||
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14]
|
||||
map[i] = j
|
||||
seq[i] = t + 1
|
||||
}
|
||||
offset += cLength
|
||||
}
|
||||
}
|
||||
|
||||
function multColumn<T>(column: Column<T>) {
|
||||
const array = column.toArray()
|
||||
return Column.ofLambda({
|
||||
value: row => array[map[row]],
|
||||
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
|
||||
rowCount, schema: column.schema
|
||||
})
|
||||
}
|
||||
|
||||
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
|
||||
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
|
||||
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
|
||||
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
|
||||
auth_seq_id: CifField.ofNumbers(seq),
|
||||
|
||||
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
|
||||
Cartn_x: CifField.ofNumbers(Cartn_x),
|
||||
Cartn_y: CifField.ofNumbers(Cartn_y),
|
||||
Cartn_z: CifField.ofNumbers(Cartn_z),
|
||||
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
|
||||
id: CifField.ofColumn(Column.ofLambda({
|
||||
value: row => row,
|
||||
areValuesEqual: (rowA, rowB) => rowA === rowB,
|
||||
rowCount, schema: d.id.schema,
|
||||
})),
|
||||
|
||||
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
|
||||
|
||||
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
|
||||
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
|
||||
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
|
||||
label_seq_id: CifField.ofNumbers(seq),
|
||||
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
|
||||
|
||||
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
|
||||
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
|
||||
|
||||
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
|
||||
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
|
||||
}
|
||||
|
||||
const categories = {
|
||||
entity: CifCategory.ofTable('entity', model.sourceData.data.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', model.sourceData.data.chem_comp),
|
||||
atom_site: CifCategory.ofFields('atom_site', _atom_site)
|
||||
}
|
||||
|
||||
return {
|
||||
header: name,
|
||||
categoryNames: Object.keys(categories),
|
||||
categories
|
||||
};
|
||||
}
|
||||
|
||||
async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model)
|
||||
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = ModelFormat.mmCIF(cif)
|
||||
const models = await _parse_mmCif(format, ctx)
|
||||
return models[0]
|
||||
})
|
||||
|
||||
const curveModel = await curveModelTask.run()
|
||||
return getStructure(curveModel)
|
||||
}
|
||||
|
||||
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
|
||||
const { name, source, results, nbCurve } = ingredient
|
||||
|
||||
@@ -133,10 +241,12 @@ async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
|
||||
const model = await getModel(source.pdb || name, baseUrl)
|
||||
if (!model) return
|
||||
|
||||
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
||||
const transforms = nbCurve ? getCurveTransforms(ingredient) : getResultTransforms(results)
|
||||
const assembly = getAssembly(transforms, structure)
|
||||
return assembly
|
||||
if (nbCurve) {
|
||||
return getCurve(name, getCurveTransforms(ingredient), model)
|
||||
} else {
|
||||
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
|
||||
return getAssembly(getResultTransforms(results), structure)
|
||||
}
|
||||
}
|
||||
|
||||
export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
|
||||
@@ -144,14 +254,16 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
const { ingredients, name } = packing
|
||||
const structures: Structure[] = []
|
||||
for (const iName in ingredients) {
|
||||
if (ctx.shouldUpdate) ctx.update(iName)
|
||||
if (ctx.shouldUpdate) await ctx.update(iName)
|
||||
const s = await getIngredientStructure(ingredients[iName], baseUrl)
|
||||
if (s) structures.push(s)
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - units`)
|
||||
const builder = Structure.Builder({ label: name })
|
||||
let offsetInvariantId = 0
|
||||
for (const s of structures) {
|
||||
if (ctx.shouldUpdate) await ctx.update(`${s.label}`)
|
||||
let maxInvariantId = 0
|
||||
for (const u of s.units) {
|
||||
const invariantId = u.invariantId + offsetInvariantId
|
||||
@@ -161,6 +273,7 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
offsetInvariantId += maxInvariantId
|
||||
}
|
||||
|
||||
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`)
|
||||
const s = builder.getStructure()
|
||||
return s
|
||||
})
|
||||
@@ -175,6 +288,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
|
||||
['influenza_model1.json', 'influenza_model1'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
||||
['curveTest', 'Curve Test'],
|
||||
]),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
preset: PD.Group({
|
||||
@@ -192,14 +306,50 @@ export const LoadCellPackModel = StateAction.build({
|
||||
|
||||
const root = state.build().toRoot();
|
||||
|
||||
const cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
let cellPackBuilder: any
|
||||
|
||||
if (params.id === 'curveTest') {
|
||||
const url = `${params.baseUrl}/extras/rna_allpoints.json`
|
||||
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
|
||||
const { points } = await (new Response(data)).json() as { points: number[] }
|
||||
const curve0: Vec3[] = []
|
||||
for (let j = 0, jl = Math.min(points.length, 3 * 100); j < jl; j += 3) {
|
||||
curve0.push(Vec3.fromArray(Vec3(), points, j))
|
||||
}
|
||||
const cell: Cell = {
|
||||
recipe: { setupfile: '', paths: [], version: '', name: 'Curve Test' },
|
||||
compartments: {
|
||||
'CurveCompartment': {
|
||||
interior: {
|
||||
ingredients: {
|
||||
'CurveIngredient': {
|
||||
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
|
||||
results: [],
|
||||
name: 'RNA',
|
||||
nbCurve: 1,
|
||||
curve0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.ImportJson, { data: cell }, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
} else {
|
||||
cellPackBuilder = root
|
||||
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
|
||||
.apply(ParseCellPack)
|
||||
|
||||
|
||||
}
|
||||
|
||||
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(taskCtx)
|
||||
const { packings } = cellPackObject.data
|
||||
let tree = state.build().to(cellPackBuilder.ref);
|
||||
const tree = state.build().to(cellPackBuilder.ref);
|
||||
|
||||
const isHiv = (
|
||||
params.id === 'BloodHIV1.0_mixed_fixed_nc1.cpr' ||
|
||||
@@ -256,7 +406,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
|
||||
if (isHiv) {
|
||||
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
|
||||
tree.apply(StateTransforms.Data.Download, { url, isBinary: true }, { state: { isGhost: true } })
|
||||
tree.apply(StateTransforms.Data.Download, { label: 'hiv_lipids', url, isBinary: true }, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
|
||||
@@ -270,7 +420,9 @@ export const LoadCellPackModel = StateAction.build({
|
||||
)
|
||||
}
|
||||
|
||||
console.time('cellpack')
|
||||
await state.updateTree(tree).runInContext(taskCtx);
|
||||
console.timeEnd('cellpack')
|
||||
}));
|
||||
|
||||
function getReprParams(ctx: PluginContext, params: { representation: 'spacefill' | 'gaussian-surface' | 'point', traceOnly: boolean }) {
|
||||
|
||||
@@ -14,8 +14,8 @@ async function parseCif(data: string|Uint8Array) {
|
||||
return parsed.result;
|
||||
}
|
||||
|
||||
async function parsePDBfile(data: string) {
|
||||
const comp = parsePDB(data);
|
||||
async function parsePDBfile(data: string, id: string) {
|
||||
const comp = parsePDB(data, id);
|
||||
const parsed = await comp.run();
|
||||
if (parsed.isError) throw parsed;
|
||||
return parsed.result;
|
||||
@@ -26,9 +26,9 @@ async function downloadCif(url: string, isBinary: boolean) {
|
||||
return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
|
||||
}
|
||||
|
||||
async function downloadPDB(url: string) {
|
||||
async function downloadPDB(url: string, id: string) {
|
||||
const data = await fetch(url);
|
||||
return parsePDBfile(await data.text());
|
||||
return parsePDBfile(await data.text(), id);
|
||||
}
|
||||
|
||||
export async function getFromPdb(id: string) {
|
||||
@@ -42,6 +42,7 @@ function getCellPackDataUrl(id: string, baseUrl: string) {
|
||||
}
|
||||
|
||||
export async function getFromCellPackDB(id: string, baseUrl: string) {
|
||||
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl));
|
||||
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id
|
||||
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name);
|
||||
return parsed;
|
||||
}
|
||||
@@ -333,6 +333,8 @@ namespace Canvas3D {
|
||||
reprRenderObjects.clear()
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
},
|
||||
|
||||
// draw,
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace Column {
|
||||
rowCount: number,
|
||||
schema: T,
|
||||
valueKind?: (row: number) => ValueKind,
|
||||
areValuesEqual?: (rowA: number, rowB: number) => boolean
|
||||
}
|
||||
|
||||
export interface ArraySpec<T extends Schema> {
|
||||
@@ -247,7 +248,7 @@ function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schem
|
||||
}
|
||||
}
|
||||
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqual, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
|
||||
return {
|
||||
schema: schema,
|
||||
__array: void 0,
|
||||
@@ -260,7 +261,7 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, sch
|
||||
for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(i + start);
|
||||
return array;
|
||||
},
|
||||
areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB)
|
||||
areValuesEqual: areValuesEqual ? areValuesEqual : (rowA, rowB) => value(rowA) === value(rowB)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,8 +177,8 @@ export namespace Lines {
|
||||
}
|
||||
|
||||
function getBoundingSphere(lineStart: Float32Array, lineEnd: Float32Array, lineCount: number, transform: Float32Array, transformCount: number) {
|
||||
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount)
|
||||
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount, 0, 4)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount, 0, 4)
|
||||
return {
|
||||
boundingSphere: Sphere3D.expandBySphere(start.boundingSphere, end.boundingSphere),
|
||||
invariantBoundingSphere: Sphere3D.expandBySphere(start.invariantBoundingSphere, end.invariantBoundingSphere)
|
||||
|
||||
@@ -89,7 +89,7 @@ export namespace Spheres {
|
||||
const padding = getMaxSize(size)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
spheres.centerBuffer.ref.value, spheres.sphereCount * 4,
|
||||
transform.aTransform.ref.value, instanceCount, padding
|
||||
transform.aTransform.ref.value, instanceCount, padding, 4
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -130,7 +130,7 @@ export namespace Spheres {
|
||||
const padding = getMaxSize(values)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, spheres.sphereCount * 4,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding, 4
|
||||
)
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -80,14 +80,15 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals
|
||||
const v = Vec3.zero()
|
||||
const boundaryHelper = new BoundaryHelper()
|
||||
|
||||
export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number): Sphere3D {
|
||||
export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number, stepFactor: number): Sphere3D {
|
||||
const step = stepFactor * 3
|
||||
boundaryHelper.reset(0)
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += 3) {
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
|
||||
Vec3.fromArray(v, position, i)
|
||||
boundaryHelper.boundaryStep(v, 0)
|
||||
}
|
||||
boundaryHelper.finishBoundaryStep()
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += 3) {
|
||||
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
|
||||
Vec3.fromArray(v, position, i)
|
||||
boundaryHelper.extendStep(v, 0)
|
||||
}
|
||||
@@ -109,8 +110,8 @@ export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere
|
||||
return boundaryHelper.getSphere()
|
||||
}
|
||||
|
||||
export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
|
||||
const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount)
|
||||
export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0, stepFactor = 1): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
|
||||
const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount, stepFactor)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount)
|
||||
Sphere3D.expand(boundingSphere, boundingSphere, padding)
|
||||
Sphere3D.expand(invariantBoundingSphere, invariantBoundingSphere, padding)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Column, ColumnHelpers } from '../../../mol-data/db'
|
||||
import { Column, ColumnHelpers, Table } from '../../../mol-data/db'
|
||||
import { Tensor } from '../../../mol-math/linear-algebra'
|
||||
import { getNumberType, NumberType, parseInt as fastParseInt, parseFloat as fastParseFloat } from '../common/text/number-parser';
|
||||
import { Encoding } from '../../common/binary-cif';
|
||||
@@ -70,6 +70,14 @@ export namespace CifCategory {
|
||||
getField(name) { return fields[name]; }
|
||||
};
|
||||
}
|
||||
|
||||
export function ofTable(name: string, table: Table<any>) {
|
||||
const fields: { [name: string]: CifField | undefined } = {}
|
||||
for (const name of table._columns) {
|
||||
fields[name] = CifField.ofColumn(table[name])
|
||||
}
|
||||
return ofFields(name, fields)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +134,7 @@ export namespace CifField {
|
||||
float,
|
||||
valueKind,
|
||||
areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB],
|
||||
toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),
|
||||
toStringArray: params => params ? ColumnHelpers.createAndFillArray(rowCount, str, params) : values as string[],
|
||||
toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params),
|
||||
toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params)
|
||||
}
|
||||
@@ -138,6 +146,14 @@ export namespace CifField {
|
||||
const float: CifField['float'] = row => values[row];
|
||||
const valueKind: CifField['valueKind'] = row => Column.ValueKind.Present;
|
||||
|
||||
const toFloatArray = (params: Column.ToArrayParams<number>) => {
|
||||
if (!params || params.array && values instanceof params.array) {
|
||||
return values as number[]
|
||||
} else {
|
||||
return ColumnHelpers.createAndFillArray(rowCount, float, params)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
__array: void 0,
|
||||
binaryEncoding: void 0,
|
||||
@@ -149,8 +165,8 @@ export namespace CifField {
|
||||
valueKind,
|
||||
areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB],
|
||||
toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),
|
||||
toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params),
|
||||
toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params)
|
||||
toIntArray: toFloatArray,
|
||||
toFloatArray
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,16 +224,22 @@ export namespace CifField {
|
||||
case 'float':
|
||||
case 'int':
|
||||
str = row => { return '' + column.value(row); };
|
||||
int = row => column.value(row);
|
||||
float = row => column.value(row);
|
||||
int = column.value;
|
||||
float = column.value;
|
||||
break
|
||||
case 'str':
|
||||
str = row => column.value(row);
|
||||
str = column.value;
|
||||
int = row => { const v = column.value(row); return fastParseInt(v, 0, v.length) || 0; };
|
||||
float = row => { const v = column.value(row); return fastParseFloat(v, 0, v.length) || 0; };
|
||||
break
|
||||
case 'list':
|
||||
const { separator } = column.schema;
|
||||
str = row => column.value(row).join(separator);
|
||||
int = row => NaN;
|
||||
float = row => NaN;
|
||||
break
|
||||
default:
|
||||
throw new Error('unsupported')
|
||||
throw new Error(`unsupported valueType '${column.schema.valueType}'`)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Vec3 } from '../../mol-math/linear-algebra/3d';
|
||||
import { Sphere3D } from './primitives/sphere3d';
|
||||
|
||||
export { CentroidHelper }
|
||||
|
||||
@@ -35,6 +36,10 @@ class CentroidHelper {
|
||||
if (d > this.radiusSq) this.radiusSq = d;
|
||||
}
|
||||
|
||||
getSphere(): Sphere3D {
|
||||
return { center: Vec3.clone(this.center), radius: Math.sqrt(this.radiusSq) };
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
@@ -1345,7 +1345,17 @@ export const SpacegroupNames: { [num: number]: SpacegroupName } = (function () {
|
||||
|
||||
// return -1 if the spacegroup does not exist.
|
||||
export function getSpacegroupIndex(nameOrNumber: number | string | SpacegroupName): number {
|
||||
const index = typeof nameOrNumber === 'number' ? nameOrNumber - 1 : ZeroBasedSpacegroupNumbers[nameOrNumber as SpacegroupName];
|
||||
let index: number
|
||||
if (typeof nameOrNumber === 'number') {
|
||||
// used by CCP4, see http://www.ccp4.ac.uk/html/pointless.html#setting
|
||||
if (nameOrNumber === 1017) index = 254
|
||||
else if (nameOrNumber === 2017) index = 255
|
||||
else if (nameOrNumber === 2018) index = 257
|
||||
else if (nameOrNumber === 3018) index = 258
|
||||
else index = nameOrNumber - 1
|
||||
} else {
|
||||
index = ZeroBasedSpacegroupNumbers[nameOrNumber as SpacegroupName];
|
||||
}
|
||||
if (typeof index === 'undefined' || typeof SpacegroupNames[index] === 'undefined') return -1;
|
||||
return index;
|
||||
}
|
||||
@@ -56,7 +56,9 @@ class Structure {
|
||||
/** Hash based on all unit.id values in the structure, reflecting the units transformation */
|
||||
transformHash: number,
|
||||
elementCount: number,
|
||||
uniqueElementCount: number,
|
||||
polymerResidueCount: number,
|
||||
polymerUnitCount: number,
|
||||
coordinateSystem: SymmetryOperator,
|
||||
label: string,
|
||||
propertyData?: any,
|
||||
@@ -64,8 +66,10 @@ class Structure {
|
||||
} = {
|
||||
hashCode: -1,
|
||||
transformHash: -1,
|
||||
elementCount: 0,
|
||||
polymerResidueCount: 0,
|
||||
elementCount: -1,
|
||||
uniqueElementCount: -1,
|
||||
polymerResidueCount: -1,
|
||||
polymerUnitCount: -1,
|
||||
coordinateSystem: SymmetryOperator.Default,
|
||||
label: ''
|
||||
};
|
||||
@@ -105,9 +109,26 @@ class Structure {
|
||||
|
||||
/** Count of all polymer residues in the structure */
|
||||
get polymerResidueCount() {
|
||||
if (this._props.polymerResidueCount === -1) {
|
||||
this._props.polymerResidueCount = getPolymerResidueCount(this)
|
||||
}
|
||||
return this._props.polymerResidueCount;
|
||||
}
|
||||
|
||||
get polymerUnitCount() {
|
||||
if (this._props.polymerUnitCount === -1) {
|
||||
this._props.polymerUnitCount = getPolymerUnitCount(this)
|
||||
}
|
||||
return this._props.polymerUnitCount;
|
||||
}
|
||||
|
||||
get uniqueElementCount() {
|
||||
if (this._props.uniqueElementCount === -1) {
|
||||
this._props.uniqueElementCount = getUniqueElementCount(this)
|
||||
}
|
||||
return this._props.uniqueElementCount;
|
||||
}
|
||||
|
||||
/** Coarse structure, defined as Containing less than twice as many elements as polymer residues */
|
||||
get isCoarse() {
|
||||
const ec = this.elementCount
|
||||
@@ -124,6 +145,7 @@ class Structure {
|
||||
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))
|
||||
@@ -221,6 +243,13 @@ class Structure {
|
||||
|| (this._props.uniqueAtomicResidueIndices = getUniqueAtomicResidueIndices(this));
|
||||
}
|
||||
|
||||
get isAtomic() {
|
||||
for (const u of this.units) {
|
||||
if (u.kind !== Unit.Kind.Atomic) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides mapping for serial element indices accross all units.
|
||||
*
|
||||
@@ -262,21 +291,19 @@ class Structure {
|
||||
}
|
||||
|
||||
getModelIndex(m: Model) {
|
||||
return this.model
|
||||
return this.models.indexOf(m)
|
||||
}
|
||||
|
||||
private initUnits(units: ArrayLike<Unit>) {
|
||||
const unitMap = IntMap.Mutable<Unit>();
|
||||
const unitIndexMap = IntMap.Mutable<number>();
|
||||
let elementCount = 0;
|
||||
let polymerResidueCount = 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;
|
||||
polymerResidueCount += u.polymerElements.length;
|
||||
if (u.id < lastId) isSorted = false;
|
||||
lastId = u.id;
|
||||
}
|
||||
@@ -285,7 +312,6 @@ class Structure {
|
||||
unitIndexMap.set(units[i].id, i);
|
||||
}
|
||||
this._props.elementCount = elementCount;
|
||||
this._props.polymerResidueCount = polymerResidueCount;
|
||||
return { unitMap, unitIndexMap };
|
||||
}
|
||||
|
||||
@@ -397,6 +423,33 @@ function getUniqueAtomicResidueIndices(structure: Structure): ReadonlyMap<UUID,
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getUniqueElementCount(structure: Structure): number {
|
||||
const { unitSymmetryGroups } = structure
|
||||
let uniqueElementCount = 0
|
||||
for (let i = 0, _i = unitSymmetryGroups.length; i < _i; i++) {
|
||||
uniqueElementCount += unitSymmetryGroups[i].elements.length
|
||||
}
|
||||
return uniqueElementCount
|
||||
}
|
||||
|
||||
function getPolymerResidueCount(structure: Structure): number {
|
||||
const { units } = structure
|
||||
let polymerResidueCount = 0
|
||||
for (let i = 0, _i = units.length; i < _i; i++) {
|
||||
polymerResidueCount += units[i].polymerElements.length;
|
||||
}
|
||||
return polymerResidueCount
|
||||
}
|
||||
|
||||
function getPolymerUnitCount(structure: Structure): number {
|
||||
const { units } = structure
|
||||
let polymerUnitCount = 0
|
||||
for (let i = 0, _i = units.length; i < _i; i++) {
|
||||
if (units[i].polymerElements.length > 0) polymerUnitCount += 1
|
||||
}
|
||||
return polymerUnitCount
|
||||
}
|
||||
|
||||
interface SerialMapping {
|
||||
/** Cummulative count of elements for each unit */
|
||||
unitElementCount: ArrayLike<number>
|
||||
@@ -572,20 +625,23 @@ namespace Structure {
|
||||
|
||||
// keeps atoms of residues together
|
||||
function partitionAtomicUnitByResidue(model: Model, indices: SortedArray, builder: StructureBuilder) {
|
||||
model.atomicHierarchy.residueAtomSegments.offsets
|
||||
const { residueAtomSegments } = model.atomicHierarchy
|
||||
|
||||
const startIndices: number[] = []
|
||||
const endIndices: number[] = []
|
||||
|
||||
const residueIt = Segmentation.transientSegments(model.atomicHierarchy.residueAtomSegments, indices)
|
||||
const residueIt = Segmentation.transientSegments(residueAtomSegments, indices)
|
||||
while (residueIt.hasNext) {
|
||||
const residueSegment = residueIt.move();
|
||||
startIndices[startIndices.length] = indices[residueSegment.start]
|
||||
endIndices[endIndices.length] = indices[residueSegment.end]
|
||||
}
|
||||
|
||||
const firstResidueAtomCount = endIndices[0] - startIndices[0]
|
||||
const gridCellCount = 512 * firstResidueAtomCount
|
||||
|
||||
const { x, y, z } = model.atomicConformation;
|
||||
const lookup = GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(startIndices) }, 8192);
|
||||
const lookup = GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(startIndices) }, gridCellCount);
|
||||
const { offset, count, array } = lookup.buckets;
|
||||
|
||||
for (let i = 0, _i = offset.length; i < _i; i++) {
|
||||
|
||||
@@ -9,15 +9,60 @@ import { Box3D, Sphere3D } from '../../../../mol-math/geometry';
|
||||
import { BoundaryHelper } from '../../../../mol-math/geometry/boundary-helper';
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import Structure from '../structure';
|
||||
import { CentroidHelper } from '../../../../mol-math/geometry/centroid-helper';
|
||||
|
||||
export type Boundary = { box: Box3D, sphere: Sphere3D }
|
||||
|
||||
const tmpBox = Box3D.empty()
|
||||
const tmpSphere = Sphere3D.zero()
|
||||
const tmpBox = Box3D.empty();
|
||||
const tmpSphere = Sphere3D.zero();
|
||||
const tmpVec = Vec3.zero();
|
||||
|
||||
const boundaryHelper = new BoundaryHelper();
|
||||
const centroidHelper = new CentroidHelper();
|
||||
|
||||
// TODO: show this be enabled? does not solve problem with "renderable bounding spheres"
|
||||
const CENTROID_COMPUTATION_THRESHOLD = -1;
|
||||
|
||||
export function computeStructureBoundary(s: Structure): Boundary {
|
||||
if (s.elementCount <= CENTROID_COMPUTATION_THRESHOLD && s.isAtomic) return computeStructureBoundaryFromElements(s);
|
||||
return computeStructureBoundaryFromUnits(s);
|
||||
}
|
||||
|
||||
function computeStructureBoundaryFromElements(s: Structure): Boundary {
|
||||
centroidHelper.reset();
|
||||
|
||||
const min = Vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE)
|
||||
const max = Vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE)
|
||||
|
||||
const v = tmpVec;
|
||||
for (const unit of s.units) {
|
||||
const { x, y, z } = unit.conformation;
|
||||
const elements = unit.elements;
|
||||
for (let j = 0, _j = elements.length; j < _j; j++) {
|
||||
const e = elements[j];
|
||||
Vec3.set(v, x(e), y(e), z(e));
|
||||
centroidHelper.includeStep(v);
|
||||
Vec3.min(min, min, v);
|
||||
Vec3.max(max, max, v);
|
||||
};
|
||||
}
|
||||
|
||||
centroidHelper.finishedIncludeStep();
|
||||
|
||||
for (const unit of s.units) {
|
||||
const { x, y, z } = unit.conformation;
|
||||
const elements = unit.elements;
|
||||
for (let j = 0, _j = elements.length; j < _j; j++) {
|
||||
const e = elements[j];
|
||||
Vec3.set(v, x(e), y(e), z(e));
|
||||
centroidHelper.radiusStep(v);
|
||||
};
|
||||
}
|
||||
|
||||
return { box: { min, max }, sphere: centroidHelper.getSphere() };
|
||||
}
|
||||
|
||||
function computeStructureBoundaryFromUnits(s: Structure): Boundary {
|
||||
const min = Vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE)
|
||||
const max = Vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE)
|
||||
|
||||
|
||||
@@ -131,6 +131,12 @@ export class PluginContext {
|
||||
substructureParent: new SubstructureParentHelper(this)
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Used to store application specific custom state which is then available
|
||||
* to State Actions and similar constructs via the PluginContext.
|
||||
*/
|
||||
readonly customState: any = Object.create(null);
|
||||
|
||||
initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) {
|
||||
try {
|
||||
this.layout.setRoot(container);
|
||||
|
||||
@@ -41,7 +41,7 @@ const Download = PluginStateTransform.BuiltIn({
|
||||
update({ oldParams, newParams, b }) {
|
||||
if (oldParams.url !== newParams.url || oldParams.isBinary !== newParams.isBinary) return StateTransformer.UpdateResult.Recreate;
|
||||
if (oldParams.label !== newParams.label) {
|
||||
(b.label as string) = newParams.label || newParams.url;
|
||||
b.label = newParams.label || newParams.url;
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
return StateTransformer.UpdateResult.Unchanged;
|
||||
@@ -237,6 +237,58 @@ const ParseDsn6 = PluginStateTransform.BuiltIn({
|
||||
}
|
||||
});
|
||||
|
||||
export { ImportString }
|
||||
type ImportString = typeof ImportString
|
||||
const ImportString = PluginStateTransform.BuiltIn({
|
||||
name: 'import-string',
|
||||
display: { name: 'Import String', description: 'Import given data as a string' },
|
||||
from: SO.Root,
|
||||
to: SO.Data.String,
|
||||
params: {
|
||||
data: PD.Value(''),
|
||||
label: PD.Optional(PD.Text('')),
|
||||
}
|
||||
})({
|
||||
apply({ params: { data, label } }) {
|
||||
return new SO.Data.String(data, { label: label || '' });
|
||||
},
|
||||
update({ oldParams, newParams, b }) {
|
||||
if (oldParams.data !== newParams.data) return StateTransformer.UpdateResult.Recreate;
|
||||
if (oldParams.label !== newParams.label) {
|
||||
b.label = newParams.label || '';
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
return StateTransformer.UpdateResult.Unchanged;
|
||||
},
|
||||
isSerializable: () => ({ isSerializable: false, reason: 'Cannot serialize user imported strings.' })
|
||||
});
|
||||
|
||||
export { ImportJson }
|
||||
type ImportJson = typeof ImportJson
|
||||
const ImportJson = PluginStateTransform.BuiltIn({
|
||||
name: 'import-json',
|
||||
display: { name: 'Import JSON', description: 'Import given data as a JSON' },
|
||||
from: SO.Root,
|
||||
to: SO.Format.Json,
|
||||
params: {
|
||||
data: PD.Value<any>({}),
|
||||
label: PD.Optional(PD.Text('')),
|
||||
}
|
||||
})({
|
||||
apply({ params: { data, label } }) {
|
||||
return new SO.Format.Json(data, { label: label || '' });
|
||||
},
|
||||
update({ oldParams, newParams, b }) {
|
||||
if (oldParams.data !== newParams.data) return StateTransformer.UpdateResult.Recreate;
|
||||
if (oldParams.label !== newParams.label) {
|
||||
b.label = newParams.label || '';
|
||||
return StateTransformer.UpdateResult.Updated;
|
||||
}
|
||||
return StateTransformer.UpdateResult.Unchanged;
|
||||
},
|
||||
isSerializable: () => ({ isSerializable: false, reason: 'Cannot serialize user imported JSON.' })
|
||||
});
|
||||
|
||||
export { ParseJson }
|
||||
type ParseJson = typeof ParseJson
|
||||
const ParseJson = PluginStateTransform.BuiltIn({
|
||||
|
||||
@@ -27,11 +27,18 @@ function opKey(l: StructureElement.Location) {
|
||||
return `${ids.sort().join(',')}|${ncs}|${hkl}|${spgrOp}`
|
||||
}
|
||||
|
||||
function splitModelEntityId(modelEntityId: string) {
|
||||
const [ modelIdx, entityId ] = modelEntityId.split('|')
|
||||
return [ parseInt(modelIdx), entityId ]
|
||||
}
|
||||
|
||||
function getSequenceWrapper(state: SequenceViewState, structureSelection: StructureElementSelectionManager): SequenceWrapper.Any | undefined {
|
||||
const { structure, entityId, invariantUnitId, operatorKey } = state
|
||||
const { structure, modelEntityId, invariantUnitId, operatorKey } = state
|
||||
const l = StructureElement.Location.create()
|
||||
const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId)
|
||||
for (const unit of structure.units) {
|
||||
StructureElement.Location.set(l, unit, unit.elements[0])
|
||||
if (structure.getModelIndex(unit.model) !== modelIdx) continue
|
||||
if (SP.entity.id(l) !== entityId) continue
|
||||
if (unit.invariantId !== invariantUnitId) continue
|
||||
if (opKey(l) !== operatorKey) continue
|
||||
@@ -43,7 +50,7 @@ function getSequenceWrapper(state: SequenceViewState, structureSelection: Struct
|
||||
}
|
||||
}
|
||||
|
||||
function getEntityOptions(structure: Structure) {
|
||||
function getModelEntityOptions(structure: Structure) {
|
||||
const options: [string, string][] = []
|
||||
const l = StructureElement.Location.create()
|
||||
const seen = new Set<string>()
|
||||
@@ -51,25 +58,33 @@ function getEntityOptions(structure: Structure) {
|
||||
for (const unit of structure.units) {
|
||||
StructureElement.Location.set(l, unit, unit.elements[0])
|
||||
const id = SP.entity.id(l)
|
||||
if (seen.has(id)) continue
|
||||
const modelIdx = structure.getModelIndex(unit.model)
|
||||
const key = `${modelIdx}|${id}`
|
||||
if (seen.has(key)) continue
|
||||
|
||||
const label = `${id}: ${SP.entity.pdbx_description(l).join(', ')}`
|
||||
options.push([ id, label ])
|
||||
seen.add(id)
|
||||
let description = SP.entity.pdbx_description(l).join(', ')
|
||||
if (description.startsWith('Polymer ') && structure.models.length > 1) {
|
||||
description += ` (${structure.models[modelIdx].entry})`
|
||||
}
|
||||
const label = `${id}: ${description}`
|
||||
options.push([ key, label ])
|
||||
seen.add(key)
|
||||
}
|
||||
|
||||
if (options.length === 0) options.push(['', 'No entities'])
|
||||
return options
|
||||
}
|
||||
|
||||
function getUnitOptions(structure: Structure, entityId: string) {
|
||||
function getUnitOptions(structure: Structure, modelEntityId: string) {
|
||||
const options: [number, string][] = []
|
||||
const l = StructureElement.Location.create()
|
||||
const seen = new Set<number>()
|
||||
const water = new Map<string, number>()
|
||||
const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId)
|
||||
|
||||
for (const unit of structure.units) {
|
||||
StructureElement.Location.set(l, unit, unit.elements[0])
|
||||
if (structure.getModelIndex(unit.model) !== modelIdx) continue
|
||||
if (SP.entity.id(l) !== entityId) continue
|
||||
|
||||
const id = unit.invariantId
|
||||
@@ -98,13 +113,15 @@ function getUnitOptions(structure: Structure, entityId: string) {
|
||||
return options
|
||||
}
|
||||
|
||||
function getOperatorOptions(structure: Structure, entityId: string, invariantUnitId: number) {
|
||||
function getOperatorOptions(structure: Structure, modelEntityId: string, invariantUnitId: number) {
|
||||
const options: [string, string][] = []
|
||||
const l = StructureElement.Location.create()
|
||||
const seen = new Set<string>()
|
||||
const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId)
|
||||
|
||||
for (const unit of structure.units) {
|
||||
StructureElement.Location.set(l, unit, unit.elements[0])
|
||||
if (structure.getModelIndex(unit.model) !== modelIdx) continue
|
||||
if (SP.entity.id(l) !== entityId) continue
|
||||
if (unit.invariantId !== invariantUnitId) continue
|
||||
|
||||
@@ -135,13 +152,13 @@ function getStructureOptions(state: State) {
|
||||
type SequenceViewState = {
|
||||
structure: Structure,
|
||||
structureRef: string,
|
||||
entityId: string,
|
||||
modelEntityId: string,
|
||||
invariantUnitId: number,
|
||||
operatorKey: string
|
||||
}
|
||||
|
||||
export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
|
||||
state = { structure: Structure.Empty, structureRef: '', entityId: '', invariantUnitId: -1, operatorKey: '' }
|
||||
state = { structure: Structure.Empty, structureRef: '', modelEntityId: '', invariantUnitId: -1, operatorKey: '' }
|
||||
|
||||
componentDidMount() {
|
||||
if (this.plugin.state.dataState.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure)).length > 0) this.setState(this.getInitialState())
|
||||
@@ -180,18 +197,18 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
|
||||
// TODO reuse selected values from previous state if applicable
|
||||
const structureRef = getStructureOptions(this.plugin.state.dataState)[0][0]
|
||||
const structure = this.getStructure(structureRef)
|
||||
const entityId = getEntityOptions(structure)[0][0]
|
||||
const invariantUnitId = getUnitOptions(structure, entityId)[0][0]
|
||||
const operatorKey = getOperatorOptions(structure, entityId, invariantUnitId)[0][0]
|
||||
return { structure, structureRef, entityId, invariantUnitId, operatorKey }
|
||||
const modelEntityId = getModelEntityOptions(structure)[0][0]
|
||||
const invariantUnitId = getUnitOptions(structure, modelEntityId)[0][0]
|
||||
const operatorKey = getOperatorOptions(structure, modelEntityId, invariantUnitId)[0][0]
|
||||
return { structure, structureRef, modelEntityId, invariantUnitId, operatorKey }
|
||||
}
|
||||
|
||||
private get params() {
|
||||
const { structure, entityId, invariantUnitId } = this.state
|
||||
const { structure, modelEntityId, invariantUnitId } = this.state
|
||||
const structureOptions = getStructureOptions(this.plugin.state.dataState)
|
||||
const entityOptions = getEntityOptions(structure)
|
||||
const unitOptions = getUnitOptions(structure, entityId)
|
||||
const operatorOptions = getOperatorOptions(structure, entityId, invariantUnitId)
|
||||
const entityOptions = getModelEntityOptions(structure)
|
||||
const unitOptions = getUnitOptions(structure, modelEntityId)
|
||||
const operatorOptions = getOperatorOptions(structure, modelEntityId, invariantUnitId)
|
||||
return {
|
||||
structure: PD.Select(structureOptions[0][0], structureOptions, { shortLabel: true }),
|
||||
entity: PD.Select(entityOptions[0][0], entityOptions, { shortLabel: true }),
|
||||
@@ -203,7 +220,7 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
|
||||
private get values(): PD.Values<SequenceView['params']> {
|
||||
return {
|
||||
structure: this.state.structureRef,
|
||||
entity: this.state.entityId,
|
||||
entity: this.state.modelEntityId,
|
||||
unit: this.state.invariantUnitId,
|
||||
operator: this.state.operatorKey
|
||||
}
|
||||
@@ -217,18 +234,18 @@ export class SequenceView extends PluginUIComponent<{ }, SequenceViewState> {
|
||||
case 'structure':
|
||||
state.structureRef = p.value
|
||||
state.structure = this.getStructure(p.value)
|
||||
state.entityId = getEntityOptions(state.structure)[0][0]
|
||||
state.invariantUnitId = getUnitOptions(state.structure, state.entityId)[0][0]
|
||||
state.operatorKey = getOperatorOptions(state.structure, state.entityId, state.invariantUnitId)[0][0]
|
||||
state.modelEntityId = getModelEntityOptions(state.structure)[0][0]
|
||||
state.invariantUnitId = getUnitOptions(state.structure, state.modelEntityId)[0][0]
|
||||
state.operatorKey = getOperatorOptions(state.structure, state.modelEntityId, state.invariantUnitId)[0][0]
|
||||
break
|
||||
case 'entity':
|
||||
state.entityId = p.value
|
||||
state.invariantUnitId = getUnitOptions(state.structure, state.entityId)[0][0]
|
||||
state.operatorKey = getOperatorOptions(state.structure, state.entityId, state.invariantUnitId)[0][0]
|
||||
state.modelEntityId = p.value
|
||||
state.invariantUnitId = getUnitOptions(state.structure, state.modelEntityId)[0][0]
|
||||
state.operatorKey = getOperatorOptions(state.structure, state.modelEntityId, state.invariantUnitId)[0][0]
|
||||
break
|
||||
case 'unit':
|
||||
state.invariantUnitId = p.value
|
||||
state.operatorKey = getOperatorOptions(state.structure, state.entityId, state.invariantUnitId)[0][0]
|
||||
state.operatorKey = getOperatorOptions(state.structure, state.modelEntityId, state.invariantUnitId)[0][0]
|
||||
break
|
||||
case 'operator':
|
||||
state.operatorKey = p.value
|
||||
|
||||
Reference in New Issue
Block a user