diff --git a/README.md b/README.md index b4d7d4ab6f1f7d42d0946be6f0932cba9586d0b2..9cbb4bd127f3aa67fefc861e3a101740f75fc1f0 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,18 @@ Run the image To see all available commands, use ``node build/model-server/preprocess -h``. -## Editor +## Development + +### Intallation + +If node complains about a missine acorn peer dependency, run the following commands + + npm update acorn --depth 20 + npm dedupe + +If the `gl` package does not compile on node 12 (there are currently no pre-built binaries) revert back to node 10. + +### Editor To get syntax highlighting for the shader files add the following to Visual Code's settings files diff --git a/package.json b/package.json index 7c62e7996d8b1acd18fc05cc0256eecf2333da7d..8065f07ddbb2505d661c80ea82462adfe4ba91a9 100644 --- a/package.json +++ b/package.json @@ -79,13 +79,15 @@ "@types/benchmark": "^1.0.31", "@types/compression": "0.0.36", "@types/express": "^4.16.1", + "@types/gl": "^4.1.0", "@types/jest": "^24.0.12", "@types/node": "^12.0.1", "@types/node-fetch": "^2.3.3", + "@types/pngjs": "^3.3.2", "@types/react": "^16.8.17", "@types/react-dom": "^16.8.4", - "@types/webgl2": "0.0.4", "@types/swagger-ui-dist": "3.0.0", + "@types/webgl2": "0.0.4", "benchmark": "^2.1.4", "circular-dependency-plugin": "^5.0.2", "concurrently": "^4.1.0", @@ -93,7 +95,7 @@ "css-loader": "^2.1.1", "extra-watch-webpack-plugin": "^1.0.3", "file-loader": "^3.0.1", - "graphql-code-generator": "^0.18.1", + "graphql-code-generator": "^0.18.2", "graphql-codegen-time": "^0.18.1", "graphql-codegen-typescript-template": "^0.18.1", "jest": "^24.8.0", @@ -116,9 +118,11 @@ "argparse": "^1.0.10", "compression": "^1.7.4", "express": "^4.16.4", + "gl": "^4.2.2", "graphql": "^14.3.0", "immutable": "^3.8.2", "node-fetch": "^2.5.0", + "pngjs": "^3.4.0", "react": "^16.8.6", "react-dom": "^16.8.6", "rxjs": "^6.5.2", diff --git a/src/apps/image-generator/index.ts b/src/apps/image-generator/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4cba746ca064f6eb2fdf7beb79649554e90fcec --- /dev/null +++ b/src/apps/image-generator/index.ts @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import * as argparse from 'argparse' +import createContext = require('gl') +import fs = require('fs') +import { PNG } from 'pngjs' +import { Canvas3D, Canvas3DParams } from 'mol-canvas3d/canvas3d'; +import InputObserver from 'mol-util/input/input-observer'; +import { ColorTheme } from 'mol-theme/color'; +import { SizeTheme } from 'mol-theme/size'; +import { CartoonRepresentationProvider } from 'mol-repr/structure/representation/cartoon'; +import CIF, { CifFrame } from 'mol-io/reader/cif' +import { trajectoryFromMmCIF } from 'mol-model-formats/structure/mmcif'; +import { Model, Structure } from 'mol-model/structure'; +import { ajaxGet } from 'mol-util/data-source'; +import { ColorNames } from 'mol-util/color/tables'; + +const width = 1024 +const height = 768 +const gl = createContext(width, height, { + alpha: false, + antialias: true, + depth: true, + preserveDrawingBuffer: true +}) + +const input = InputObserver.create() +const canvas3d = Canvas3D.create(gl, input, { + multiSample: 'on', + sampleLevel: 3, + renderer: { + ...Canvas3DParams.renderer.defaultValue, + lightIntensity: 0, + ambientIntensity: 1, + backgroundColor: ColorNames.white + }, + postprocessing: { + ...Canvas3DParams.postprocessing.defaultValue, + occlusionEnable: true, + outlineEnable: true + } +}) +canvas3d.animate() + +const reprCtx = { + wegbl: canvas3d.webgl, + colorThemeRegistry: ColorTheme.createRegistry(), + sizeThemeRegistry: SizeTheme.createRegistry() +} +function getCartoonRepr() { + return CartoonRepresentationProvider.factory(reprCtx, CartoonRepresentationProvider.getParams) +} + +async function parseCif(data: string|Uint8Array) { + const comp = CIF.parse(data); + const parsed = await comp.run(); + if (parsed.isError) throw parsed; + return parsed.result; +} + +async function downloadCif(url: string, isBinary: boolean) { + const data = await ajaxGet({ url, type: isBinary ? 'binary' : 'string' }).run(); + return parseCif(data); +} + +async function downloadFromPdb(pdb: string) { + const parsed = await downloadCif(`https://files.rcsb.org/download/${pdb}.cif`, false); + // const parsed = await downloadCif(`https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${pdb}`, true); + return parsed.blocks[0]; +} + +async function getModels(frame: CifFrame) { + return await trajectoryFromMmCIF(frame).run(); +} + +async function getStructure(model: Model) { + return Structure.ofModel(model); +} + +async function run(id: string, out: string) { + try { + const cif = await downloadFromPdb(id) + const models = await getModels(cif) + const structure = await getStructure(models[0]) + + const cartoonRepr = getCartoonRepr() + cartoonRepr.setTheme({ + color: reprCtx.colorThemeRegistry.create('sequence-id', { structure }), + size: reprCtx.sizeThemeRegistry.create('uniform', { structure }) + }) + await cartoonRepr.createOrUpdate({ ...CartoonRepresentationProvider.defaultValues, quality: 'auto' }, structure).run() + + canvas3d.add(cartoonRepr) + canvas3d.resetCamera() + } catch (e) { + console.log(e) + process.exit(1) + } + + setTimeout(() => { + const pixelData = canvas3d.getPixelData('draw') + const png = new PNG({ width, height }) + png.data = Buffer.from(pixelData.array) + png.pack().pipe(fs.createWriteStream(out)).on('finish', () => { + process.exit() + }) + }, 2000) +} + +// + +const parser = new argparse.ArgumentParser({ + addHelp: true, + description: 'render image as PNG (work in progress)' +}); +parser.addArgument([ '--id', '-i' ], { + required: true, + help: 'PDB ID' +}); +parser.addArgument([ '--out', '-o' ], { + required: true, + help: 'image output path' +}); + +interface Args { + id: string + out: string +} +const args: Args = parser.parseArgs(); + +run(args.id, args.out) \ No newline at end of file