Bladeren bron

Merge branch 'master' of https://github.com/molstar/molstar into eachLocation

Alexander Rose 2 jaren geleden
bovenliggende
commit
c5c8de8628

+ 4 - 0
CHANGELOG.md

@@ -13,6 +13,10 @@ Note that since we don't clearly distinguish between a public and private interf
 - Fix rendering issues caused by VAO reuse
 - Add "Zoom All", "Orient Axes", "Reset Axes" buttons to the "Reset Camera" button
 - Improve trackball move-state handling when key bindings use modifiers
+- Fix rendering with very small viewport and SSAO enabled
+- Fix `.getAllLoci` for structure representations with `structure.child`
+- Fix `readAllLinesAsync` refering to dom length property
+- Make mol-util/file-info node compatible
 - Add `eachLocation` to representation/visual interface
 
 ## [v3.33.0] - 2023-04-02

+ 1 - 1
src/extensions/volumes-and-segmentations/index.ts

@@ -19,7 +19,7 @@ import { VolsegEntryFromRoot, VolsegGlobalStateFromRoot, VolsegStateFromEntry }
 import { VolsegUI } from './ui';
 
 
-const DEBUGGING = window.location.hostname === 'localhost';
+const DEBUGGING = typeof window !== 'undefined' ? window?.location?.hostname === 'localhost' : false;
 
 export const VolsegVolumeServerConfig = {
     // DefaultServer: new PluginConfigItem('volseg-volume-server', DEFAULT_VOLUME_SERVER_V2),

+ 8 - 8
src/mol-canvas3d/passes/postprocessing.ts

@@ -506,11 +506,11 @@ export class PostprocessingPass {
         const sw = Math.floor(width * this.ssaoScale);
         const sh = Math.floor(height * this.ssaoScale);
 
-        const hw = Math.floor(sw * 0.5);
-        const hh = Math.floor(sh * 0.5);
+        const hw = Math.max(1, Math.floor(sw * 0.5));
+        const hh = Math.max(1, Math.floor(sh * 0.5));
 
-        const qw = Math.floor(sw * 0.25);
-        const qh = Math.floor(sh * 0.25);
+        const qw = Math.max(1, Math.floor(sw * 0.25));
+        const qh = Math.max(1, Math.floor(sh * 0.25));
 
         this.downsampledDepthTarget = drawPass.packedDepth
             ? webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear', 'rgba')
@@ -562,12 +562,12 @@ export class PostprocessingPass {
             this.ssaoDepthTexture.define(sw, sh);
             this.ssaoDepthBlurProxyTexture.define(sw, sh);
 
-            const hw = Math.floor(sw * 0.5);
-            const hh = Math.floor(sh * 0.5);
+            const hw = Math.max(1, Math.floor(sw * 0.5));
+            const hh = Math.max(1, Math.floor(sh * 0.5));
             this.depthHalfTarget.setSize(hw, hh);
 
-            const qw = Math.floor(sw * 0.25);
-            const qh = Math.floor(sh * 0.25);
+            const qw = Math.max(1, Math.floor(sw * 0.25));
+            const qh = Math.max(1, Math.floor(sh * 0.25));
             this.depthQuarterTarget.setSize(qw, qh);
 
             ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));

+ 1 - 1
src/mol-io/common/file-handle.ts

@@ -16,7 +16,7 @@ export interface FileHandle {
      * @param position The offset from the beginning of the file from which data should be read.
      * @param sizeOrBuffer The buffer the data will be read from.
      * @param length The number of bytes to read.
-     * @param byteOffset The offset in the buffer at which to start writing.
+     * @param byteOffset The offset in the buffer at which to start reading.
      */
     readBuffer(position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number): Promise<{ bytesRead: number, buffer: SimpleBuffer }>
 

+ 2 - 3
src/mol-io/reader/common/text/tokenizer.ts

@@ -134,7 +134,6 @@ namespace Tokenizer {
 
     /** Advance the state by the given number of lines and return line starts/ends as tokens. */
     export async function readLinesAsync(state: Tokenizer, count: number, ctx: RuntimeContext, initialLineCount = 100000): Promise<Tokens> {
-        const { length } = state;
         const lineTokens = TokenBuilder.create(state.data, count * 2);
 
         let linesAlreadyRead = 0;
@@ -143,7 +142,7 @@ namespace Tokenizer {
             readLinesChunk(state, linesToRead, lineTokens);
             linesAlreadyRead += linesToRead;
             return linesToRead;
-        }, (ctx, state) => ctx.update({ message: 'Parsing...', current: state.position, max: length }));
+        }, (ctx, state) => ctx.update({ message: 'Parsing...', current: state.position, max: state.length }));
 
         return lineTokens;
     }
@@ -174,7 +173,7 @@ namespace Tokenizer {
         await chunkedSubtask(ctx, chunkSize, state, (chunkSize, state) => {
             readLinesChunkChecked(state, chunkSize, tokens);
             return state.position < state.length ? chunkSize : 0;
-        }, (ctx, state) => ctx.update({ message: 'Parsing...', current: state.position, max: length }));
+        }, (ctx, state) => ctx.update({ message: 'Parsing...', current: state.position, max: state.length }));
 
         return tokens;
     }

+ 4 - 4
src/mol-plugin-state/actions/file.ts

@@ -8,13 +8,13 @@ import { PluginContext } from '../../mol-plugin/context';
 import { StateAction } from '../../mol-state';
 import { Task } from '../../mol-task';
 import { Asset } from '../../mol-util/assets';
-import { getFileInfo } from '../../mol-util/file-info';
+import { getFileNameInfo } from '../../mol-util/file-info';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { unzip } from '../../mol-util/zip/zip';
 import { PluginStateObject } from '../objects';
 
 async function processFile(file: Asset.File, plugin: PluginContext, format: string, visuals: boolean) {
-    const info = getFileInfo(file.file!);
+    const info = getFileNameInfo(file.file?.name ?? '');
     const isBinary = plugin.dataFormats.binaryExtensions.has(info.ext);
     const { data } = await plugin.builders.data.readFile({ file, isBinary });
     const provider = format === 'auto'
@@ -111,8 +111,8 @@ export const DownloadFile = StateAction.build({
                     }
                 } else {
                     const url = Asset.getUrl(params.url);
-                    const info = getFileInfo(url);
-                    await processFile(Asset.File(new File([data.obj?.data as Uint8Array], info.name)), plugin, 'auto', params.visuals);
+                    const fileName = getFileNameInfo(url).name;
+                    await processFile(Asset.File(new File([data.obj?.data as Uint8Array], fileName)), plugin, 'auto', params.visuals);
                 }
             } else {
                 const provider = plugin.dataFormats.get(params.format);

+ 3 - 3
src/mol-plugin-state/actions/structure.ts

@@ -18,7 +18,7 @@ import { Download } from '../transforms/data';
 import { CustomModelProperties, CustomStructureProperties, ModelFromTrajectory, TrajectoryFromModelAndCoordinates } from '../transforms/model';
 import { Asset } from '../../mol-util/assets';
 import { PluginConfig } from '../../mol-plugin/config';
-import { getFileInfo } from '../../mol-util/file-info';
+import { getFileNameInfo } from '../../mol-util/file-info';
 import { assertUnreachable } from '../../mol-util/type-helpers';
 import { TopologyFormatCategory } from '../formats/topology';
 import { CoordinatesFormatCategory } from '../formats/coordinates';
@@ -184,7 +184,7 @@ const DownloadStructure = StateAction.build({
             for (const download of downloadParams) {
                 const data = await plugin.builders.data.download(download, { state: { isGhost: true } });
                 const provider = format === 'auto'
-                    ? plugin.dataFormats.auto(getFileInfo(Asset.getUrl(download.url)), data.cell?.obj!)
+                    ? plugin.dataFormats.auto(getFileNameInfo(Asset.getUrl(download.url)), data.cell?.obj!)
                     : plugin.dataFormats.get(format);
                 if (!provider) throw new Error('unknown file format');
                 const trajectory = await plugin.builders.structure.parseTrajectory(data, provider);
@@ -385,7 +385,7 @@ export const LoadTrajectory = StateAction.build({
         const processFile = async (file: Asset.File | null) => {
             if (!file) throw new Error('No file selected');
 
-            const info = getFileInfo(file.file!);
+            const info = getFileNameInfo(file.file?.name ?? '');
             const isBinary = ctx.dataFormats.binaryExtensions.has(info.ext);
             const { data } = await ctx.builders.data.readFile({ file, isBinary });
             const provider = ctx.dataFormats.auto(info, data.cell?.obj!);

+ 2 - 2
src/mol-plugin-state/actions/volume.ts

@@ -8,7 +8,7 @@
 import { PluginContext } from '../../mol-plugin/context';
 import { StateAction, StateTransformer, StateSelection } from '../../mol-state';
 import { Task } from '../../mol-task';
-import { getFileInfo } from '../../mol-util/file-info';
+import { getFileNameInfo } from '../../mol-util/file-info';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { PluginStateObject } from '../objects';
 import { Download } from '../transforms/data';
@@ -119,7 +119,7 @@ const DownloadDensity = StateAction.build({
     switch (src.name) {
         case 'url':
             downloadParams = src.params;
-            provider = src.params.format === 'auto' ? plugin.dataFormats.auto(getFileInfo(Asset.getUrl(downloadParams.url)), data.cell?.obj!) : plugin.dataFormats.get(src.params.format);
+            provider = src.params.format === 'auto' ? plugin.dataFormats.auto(getFileNameInfo(Asset.getUrl(downloadParams.url)), data.cell?.obj!) : plugin.dataFormats.get(src.params.format);
             break;
         case 'pdb-xray':
             entryId = src.params.provider.id;

+ 2 - 2
src/mol-plugin-state/builder/data.ts

@@ -7,7 +7,7 @@
 import { StateTransformer, StateTransform } from '../../mol-state';
 import { PluginContext } from '../../mol-plugin/context';
 import { Download, ReadFile, DownloadBlob, RawData } from '../transforms/data';
-import { getFileInfo } from '../../mol-util/file-info';
+import { getFileNameInfo } from '../../mol-util/file-info';
 
 export class DataBuilder {
     private get dataState() {
@@ -31,7 +31,7 @@ export class DataBuilder {
 
     async readFile(params: StateTransformer.Params<ReadFile>, options?: Partial<StateTransform.Options>) {
         const data = await this.dataState.build().toRoot().apply(ReadFile, params, options).commit({ revertOnError: true });
-        const fileInfo = getFileInfo(params.file?.file || '');
+        const fileInfo = getFileNameInfo(params.file?.file?.name ?? '');
         return { data: data, fileInfo };
     }
 

+ 3 - 3
src/mol-plugin-state/formats/provider.ts

@@ -8,7 +8,7 @@
 import { decodeMsgPack } from '../../mol-io/common/msgpack/decode';
 import { PluginContext } from '../../mol-plugin/context';
 import { StateObjectRef } from '../../mol-state';
-import { FileInfo } from '../../mol-util/file-info';
+import { FileNameInfo } from '../../mol-util/file-info';
 import { PluginStateObject } from '../objects';
 
 export interface DataFormatProvider<P = any, R = any, V = any> {
@@ -17,7 +17,7 @@ export interface DataFormatProvider<P = any, R = any, V = any> {
     category?: string,
     stringExtensions?: string[],
     binaryExtensions?: string[],
-    isApplicable?(info: FileInfo, data: string | Uint8Array): boolean,
+    isApplicable?(info: FileNameInfo, data: string | Uint8Array): boolean,
     parse(plugin: PluginContext, data: StateObjectRef<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, params?: P): Promise<R>,
     visuals?(plugin: PluginContext, data: R): Promise<V> | undefined
 }
@@ -25,7 +25,7 @@ export interface DataFormatProvider<P = any, R = any, V = any> {
 export function DataFormatProvider<P extends DataFormatProvider>(provider: P): P { return provider; }
 
 type cifVariants = 'dscif' | 'segcif' | 'coreCif' | -1
-export function guessCifVariant(info: FileInfo, data: Uint8Array | string): cifVariants {
+export function guessCifVariant(info: FileNameInfo, data: Uint8Array | string): cifVariants {
     if (info.ext === 'bcif') {
         try {
             // TODO: find a way to run msgpackDecode only once

+ 2 - 2
src/mol-plugin-state/formats/registry.ts

@@ -5,7 +5,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { FileInfo } from '../../mol-util/file-info';
+import { FileNameInfo } from '../../mol-util/file-info';
 import { PluginStateObject } from '../objects';
 import { DataFormatProvider } from './provider';
 import { BuiltInTrajectoryFormats } from './trajectory';
@@ -78,7 +78,7 @@ export class DataFormatRegistry {
         this._map.delete(name);
     }
 
-    auto(info: FileInfo, dataStateObject: PluginStateObject.Data.Binary | PluginStateObject.Data.String) {
+    auto(info: FileNameInfo, dataStateObject: PluginStateObject.Data.Binary | PluginStateObject.Data.String) {
         for (let i = 0, il = this.list.length; i < il; ++i) {
             const p = this._list[i].provider;
 

+ 1 - 1
src/mol-repr/structure/complex-representation.ts

@@ -78,7 +78,7 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
     }
 
     function getAllLoci() {
-        return [Structure.Loci(_structure.target)];
+        return [Structure.Loci(_structure.child ?? _structure)];
     }
 
     function eachLocation(cb: LocationCallback) {

+ 1 - 1
src/mol-repr/structure/units-representation.ts

@@ -202,7 +202,7 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
     }
 
     function getAllLoci() {
-        return [Structure.Loci(_structure.target)];
+        return [Structure.Loci(_structure.child ?? _structure)];
     }
 
     function mark(loci: Loci, action: MarkerAction) {

+ 29 - 0
src/mol-util/_spec/file-info.spec.ts

@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Russell Parker <russell@benchling.com>
+ */
+
+import { getFileNameInfo } from '../file-info';
+
+describe('getFileNameInfo', () => {
+    it('handles empty string', () => {
+        expect(getFileNameInfo('')).toEqual({ path: '', name: '', ext: '', base: '', dir: '', protocol: '', query: '' });
+    });
+
+    it('handles url', () => {
+        expect(getFileNameInfo('https://models.rcsb.org/4KTC.bcif')).toEqual({ path: 'models.rcsb.org/4KTC.bcif', name: '4KTC.bcif', ext: 'bcif', base: '4KTC', dir: 'models.rcsb.org/', protocol: 'https', query: '' })
+    });
+
+    it('handles compressed url', () => {
+        expect(getFileNameInfo('https://files.rcsb.org/download/7QPD.cif.gz?foo=bar')).toEqual({ path: 'files.rcsb.org/download/7QPD.cif.gz', name: '7QPD.cif.gz', ext: 'cif', base: '7QPD', dir: 'files.rcsb.org/download/', protocol: 'https', query: '?foo=bar' })
+    });
+
+    it('handles local path', () => {
+        expect(getFileNameInfo('/usr/local/data/structure.pdb')).toEqual({ path: '/usr/local/data/structure.pdb', name: 'structure.pdb', ext: 'pdb', base: 'structure', dir: '/usr/local/data/', protocol: '', query: '' })
+    });
+
+    it('handles local path with protocol', () => {
+        expect(getFileNameInfo('file:///usr/local/data/structure.pdb')).toEqual({ path: '/usr/local/data/structure.pdb', name: 'structure.pdb', ext: 'pdb', base: 'structure', dir: '/usr/local/data/', protocol: 'file', query: '' })
+    });
+});

+ 10 - 18
src/mol-util/file-info.ts

@@ -1,17 +1,15 @@
 /**
- * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Russell Parker <russell@benchling.com>
  */
 
-/** A File or Blob object or a URL string */
-export type FileInput = File | Blob | string
-
 // TODO only support compressed files for which uncompression support is available???
 // TODO store globally with decompression plugins?
-const compressedExtList = ['gz', 'zip'];
+const COMPRESSED_EXT_LIST = ['gz', 'zip'];
 
-export interface FileInfo {
+export interface FileNameInfo {
     path: string
     name: string
     ext: string
@@ -19,20 +17,12 @@ export interface FileInfo {
     dir: string
     protocol: string
     query: string
-    src: FileInput
 }
 
-export function getFileInfo(file: FileInput): FileInfo {
-    let path: string;
+export function getFileNameInfo(fileName: string): FileNameInfo {
+    let path: string = fileName;
     let protocol = '';
 
-    if (file instanceof File) {
-        path = file.name;
-    } else if (file instanceof Blob) {
-        path = '';
-    } else {
-        path = file;
-    }
     const queryIndex = path.lastIndexOf('?');
     const query = queryIndex !== -1 ? path.substring(queryIndex) : '';
     path = path.substring(0, queryIndex === -1 ? path.length : queryIndex);
@@ -51,12 +41,14 @@ export function getFileInfo(file: FileInput): FileInfo {
 
     const dir = path.substring(0, path.lastIndexOf('/') + 1);
 
-    if (compressedExtList.includes(ext)) {
+    if (COMPRESSED_EXT_LIST.includes(ext)) {
         const n = path.length - ext.length - 1;
+        // TODO: change logic to String.prototype.substring since substr is deprecated
         ext = (path.substr(0, n).split('.').pop() || '').toLowerCase();
         const m = base.length - ext.length - 1;
         base = base.substr(0, m);
     }
 
-    return { path, name, ext, base, dir, protocol, query, src: file };
+    // Note: it appears that most of this data never gets used.
+    return { path, name, ext, base, dir, protocol, query };
 }