diff --git a/src/apps/render-test/components/file-input.tsx b/src/apps/render-test/components/file-input.tsx index f83d200ab54414550b912eade40e726ce625d7e3..850ba452e38fe881a9a0c32dc723589e7e4c5658 100644 --- a/src/apps/render-test/components/file-input.tsx +++ b/src/apps/render-test/components/file-input.tsx @@ -7,7 +7,7 @@ import * as React from 'react' import { WithStyles } from 'material-ui/styles'; import TextField from 'material-ui/TextField'; -// import FileUpload from '@material-ui/icons/FileUpload'; +import Button from 'material-ui/Button'; import State from '../state' import Observer from './observer'; @@ -24,17 +24,39 @@ export default class FileInput extends Observer<{ state: State } & WithStyles, { render() { const { classes, state } = this.props; - return <TextField - label='PDB ID' - className={classes.textField} - disabled={this.state.loading} - margin='normal' - onChange={(event) => { - state.pdbId = event.target.value - }} - onKeyPress={(event) => { - if (event.key === 'Enter') state.loadPdbId() - }} - /> + return <div> + <TextField + label='PDB ID' + className={classes.textField} + disabled={this.state.loading} + margin='normal' + onChange={(event) => { + state.pdbId = event.target.value + }} + onKeyPress={(event) => { + if (event.key === 'Enter') state.loadPdbId() + }} + /> + <input + accept='*.cif' + className={classes.input} + id='button-file' + type='file' + onChange={(event) => { + if (event.target.files) { + state.loadFile(event.target.files[0]) + } + }} + /> + <label htmlFor='button-file'> + <Button + variant='raised' + component='span' + className={classes.button} + > + Open CIF + </Button> + </label> + </div> } } \ No newline at end of file diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index c8a2f0cd6ed84df02a1e9734db2ad346164eeea4..dabfc2729b650e9dcf8207b9942327f2ecab0db0 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -17,10 +17,10 @@ import Spacefill, { SpacefillProps } from 'mol-geo/representation/structure/spac import Point, { PointProps } from 'mol-geo/representation/structure/point' import { Run } from 'mol-task' -import { Symmetry } from 'mol-model/structure' +import { Symmetry, Structure } from 'mol-model/structure' // import mcubes from './utils/mcubes' -import { getStructuresFromPdbId, log } from './utils' +import { getStructuresFromPdbId, getStructuresFromFile, log } from './utils' import { StructureRepresentation } from 'mol-geo/representation/structure'; // import Cylinder from 'mol-geo/primitive/cylinder'; @@ -77,15 +77,11 @@ export default class State { this.viewer.animate() } - async loadPdbId () { - const { viewer, pdbId, loading } = this + async initStructure (structure: Structure) { + const { viewer, loading } = this viewer.clear() - if (pdbId.length !== 4) return - loading.next(true) - - const structures = await getStructuresFromPdbId(pdbId) - const struct = await Run(Symmetry.buildAssembly(structures[0], '1'), log, 100) + const struct = await Run(Symmetry.buildAssembly(structure, '1'), log, 100) this.pointRepr = StructureRepresentation(Point) await Run(this.pointRepr.create(struct, this.getPointProps()), log, 100) @@ -102,6 +98,23 @@ export default class State { loading.next(false) } + async loadFile (file: File) { + this.viewer.clear() + this.loading.next(true) + + const structures = await getStructuresFromFile(file) + this.initStructure(structures[0]) + } + + async loadPdbId () { + this.viewer.clear() + if (this.pdbId.length !== 4) return + this.loading.next(true) + + const structures = await getStructuresFromPdbId(this.pdbId) + this.initStructure(structures[0]) + } + async update () { if (!this.spacefillRepr) return await Run(this.spacefillRepr.update(this.getSpacefillProps()), log, 100) diff --git a/src/apps/render-test/ui.tsx b/src/apps/render-test/ui.tsx index e65ea8e287ba2ab0667ccfc3edeb61bb3b90e873..948a49d2f0d1133b5c48c0ab90bde9bc9a2ada71 100644 --- a/src/apps/render-test/ui.tsx +++ b/src/apps/render-test/ui.tsx @@ -52,6 +52,12 @@ const styles: StyleRulesCallback = (theme: Theme) => ({ marginRight: theme.spacing.unit, width: 200, }, + button: { + margin: theme.spacing.unit, + }, + input: { + display: 'none', + }, } as any); const decorate = withStyles(styles); diff --git a/src/apps/render-test/utils/index.ts b/src/apps/render-test/utils/index.ts index 67a7eb9a307501f67f245856ee0c58a5e4a8716a..738bd7851a542c6522ccd07cb869a73e11ee6891 100644 --- a/src/apps/render-test/utils/index.ts +++ b/src/apps/render-test/utils/index.ts @@ -24,4 +24,21 @@ export async function getStructuresFromPdbId(pdbid: string) { const data = await fetch(`https://files.rcsb.org/download/${pdbid}.cif`) const parsed = await parseCif(await data.text()) return Structure.ofData({ kind: 'mmCIF', data: CIF.schema.mmCIF(parsed.result.blocks[0]) }) +} + +const readFileAsText = (file: File) => { + const fileReader = new FileReader() + return new Promise<string>((resolve, reject) => { + fileReader.onerror = () => { + fileReader.abort() + reject(new DOMException('Error parsing input file.')) + } + fileReader.onload = () => resolve(fileReader.result) + fileReader.readAsText(file) + }) +} + +export async function getStructuresFromFile(file: File) { + const parsed = await parseCif(await readFileAsText(file)) + return Structure.ofData({ kind: 'mmCIF', data: CIF.schema.mmCIF(parsed.result.blocks[0]) }) } \ No newline at end of file