diff --git a/src/mol-geo/util/marching-cubes/algorithm.ts b/src/mol-geo/util/marching-cubes/algorithm.ts index 2e4b5e4de34d41391f715b98775320accbea1bb0..2c79d5ae95be1eadbaab65278651b20c1d97c4d9 100644 --- a/src/mol-geo/util/marching-cubes/algorithm.ts +++ b/src/mol-geo/util/marching-cubes/algorithm.ts @@ -196,11 +196,16 @@ class MarchingCubesState { const n1y = sfg(sf, hi, Math.max(0, hj - 1), hk) - sfg(sf, hi, Math.min(this.nY - 1, hj + 1), hk); const n1z = sfg(sf, hi, hj, Math.max(0, hk - 1)) - sfg(sf, hi, hj, Math.min(this.nZ - 1, hk + 1)); - this.builder.addNormal( - n0x + t * (n0x - n1x), - n0y + t * (n0y - n1y), - n0z + t * (n0z - n1z) - ); + const nx = n0x + t * (n0x - n1x); + const ny = n0y + t * (n0y - n1y); + const nz = n0z + t * (n0z - n1z); + + // ensure normal-direction is the same for negative and positive iso-levels + if (this.isoLevel >= 0) { + this.builder.addNormal(nx, ny, nz); + } else { + this.builder.addNormal(-nx, -ny, -nz); + } return id; } @@ -255,7 +260,13 @@ class MarchingCubesState { const triInfo = TriTable[tableIndex]; for (let t = 0; t < triInfo.length; t += 3) { - this.builder.addTriangle(this.vertList, triInfo[t], triInfo[t + 1], triInfo[t + 2], edgeFilter); + const l = triInfo[t], m = triInfo[t + 1], n = triInfo[t + 2]; + // ensure winding-order is the same for negative and positive iso-levels + if (this.isoLevel >= 0) { + this.builder.addTriangle(this.vertList, l, m, n, edgeFilter); + } else { + this.builder.addTriangle(this.vertList, n, m, l, edgeFilter); + } } } } \ No newline at end of file diff --git a/src/mol-model-formats/structure/pdb/assembly.ts b/src/mol-model-formats/structure/pdb/assembly.ts index 64f2ac42c5a4e0cab8efb9e0874f382bd2fcc224..2bd23d554bcdad13da0bec063d9c5e4cb4b86f83 100644 --- a/src/mol-model-formats/structure/pdb/assembly.ts +++ b/src/mol-model-formats/structure/pdb/assembly.ts @@ -59,13 +59,13 @@ export function parseRemark350(lines: Tokens, lineStart: number, lineEnd: number const assemblies: PdbAssembly[] = []; // Read the assemblies - let current: PdbAssembly, group: PdbAssembly['groups'][0], matrix: Mat4, operId = 1; + let current: PdbAssembly, group: PdbAssembly['groups'][0], matrix: Mat4, operId = 1, asmId = 1; const getLine = (n: number) => lines.data.substring(lines.indices[2 * n], lines.indices[2 * n + 1]); for (let i = lineStart; i < lineEnd; i++) { let line = getLine(i); if (line.substr(11, 12) === 'BIOMOLECULE:') { const id = line.substr(23).trim(); - let details: string = `Biomolecule ` + id; + let details = `Biomolecule ${id}`; line = getLine(i + 1); if (line.substr(11, 30) !== 'APPLY THE FOLLOWING TO CHAINS:') { i++; @@ -100,6 +100,23 @@ export function parseRemark350(lines: Tokens, lineStart: number, lineEnd: number const c = chainList[j].trim(); if (c) group!.chains.push(c); } + } else if (line.substr(11, 33) === 'APPLYING THE FOLLOWING TO CHAINS:') { + // variant in older PDB format version + current = PdbAssembly(`${asmId}`, `Biomolecule ${asmId}`); + assemblies.push(current); + asmId += 1; + + group = { chains: [], operators: [] }; + current!.groups.push(group); + + i++; + line = getLine(i); + + const chainList = line.substr(11, 69).split(','); + for (let j = 0, jl = chainList.length; j < jl; ++j) { + const c = chainList[j].trim(); + if (c) group!.chains.push(c); + } } } diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 859375f5f0f3042446f45cc059348d8312615f59..2d4a95177e551e1fc1e2dfc9cafc257772287e5c 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -25,6 +25,7 @@ import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif'; import { ChainIndex } from './indexing'; import { SymmetryOperator } from '../../../mol-math/geometry'; import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry'; +import { Column } from '../../../mol-data/db'; /** * Interface to the "source data" of the molecule. @@ -233,18 +234,24 @@ export namespace Model { } export function hasDensityMap(model: Model): boolean { + if (!MmcifFormat.is(model.sourceData)) return false; + return hasXrayMap(model) || hasEmMap(model); + } + + export function probablyHasDensityMap(model: Model): boolean { if (!MmcifFormat.is(model.sourceData)) return false; const { db } = model.sourceData.data; - return ( - hasXrayMap(model) || hasEmMap(model) || ( - // check if from pdb archive but missing relevant meta data - isFromPdbArchive(model) && - !db.pdbx_database_related.db_name.isDefined && - !db.pdbx_database_status.status_code_sf.isDefined && ( - !db.exptl.method.isDefined || - isFromXray(model) || - isFromEm(model) - ) + return hasDensityMap(model) || ( + // check if from pdb archive but missing relevant meta data + isFromPdbArchive(model) && ( + !db.exptl.method.isDefined || + (isFromXray(model) && ( + !db.pdbx_database_status.status_code_sf.isDefined || + db.pdbx_database_status.status_code_sf.valueKind(0) === Column.ValueKind.Unknown + )) || + (isFromEm(model) && ( + !db.pdbx_database_related.db_name.isDefined + )) ) ); } diff --git a/src/mol-plugin-state/formats/volume.ts b/src/mol-plugin-state/formats/volume.ts index 914a7eed1671103d52227cfa1ab0fd7d2a9b6c4a..a89920a3684e7ea0c7fc7cb3b322cfd0166de140 100644 --- a/src/mol-plugin-state/formats/volume.ts +++ b/src/mol-plugin-state/formats/volume.ts @@ -181,7 +181,7 @@ export const DscifProvider = DataFormatProvider({ export const BuiltInVolumeFormats = [ ['ccp4', Ccp4Provider] as const, - ['dns6', Dsn6Provider] as const, + ['dsn6', Dsn6Provider] as const, ['cube', CubeProvider] as const, ['dx', DxProvider] as const, ['dscif', DscifProvider] as const, diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts index fd1bd1db6354d8b83e6c8b0c272981e1434e7eef..99c01876329954d0d043927e15d41e5f24b0b1a6 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts @@ -54,7 +54,7 @@ export const InitVolumeStreaming = StateAction.build({ isApplicable: (a, _, plugin: PluginContext) => { const canStreamTest = plugin.config.get(PluginConfig.VolumeStreaming.CanStream); if (canStreamTest) return canStreamTest(a.data, plugin); - return a.data.models.length === 1 && Model.hasDensityMap(a.data.models[0]); + return a.data.models.length === 1 && Model.probablyHasDensityMap(a.data.models[0]); } })(({ ref, state, params }, plugin: PluginContext) => Task.create('Volume Streaming', async taskCtx => { const entries: InfoEntryProps[] = []; diff --git a/src/mol-plugin/config.ts b/src/mol-plugin/config.ts index f63b5dc835d748fad98c0fd5f7f8bc61dbbbb525..52a83c6093a2aa88a39263b30dec7706ce0f1917 100644 --- a/src/mol-plugin/config.ts +++ b/src/mol-plugin/config.ts @@ -28,9 +28,7 @@ export const PluginConfig = { VolumeStreaming: { DefaultServer: item('volume-streaming.server', 'https://ds.litemol.org'), CanStream: item('volume-streaming.can-stream', (s: Structure, plugin: PluginContext) => { - return s.models.length === 1 && (Model.hasDensityMap(s.models[0]) - // the following test is to include e.g. 'updated' files from PDBe - || (!Model.isFromPdbArchive(s.models[0]) && s.models[0].entryId.length === 4)); + return s.models.length === 1 && Model.probablyHasDensityMap(s.models[0]); }), EmdbHeaderServer: item('volume-streaming.emdb-header-server', 'https://ftp.wwpdb.org/pub/emdb/structures'), },