Skip to content
Snippets Groups Projects
Commit 730337ac authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, react components

parent 01bdf118
No related branches found
No related tags found
No related merge requests found
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
...@@ -80,8 +80,10 @@ ...@@ -80,8 +80,10 @@
"dependencies": { "dependencies": {
"argparse": "^1.0.10", "argparse": "^1.0.10",
"express": "^4.16.3", "express": "^4.16.3",
"material-ui": "^1.0.0-beta.41",
"node-fetch": "^2.1.2", "node-fetch": "^2.1.2",
"react": "^16.3.1", "react": "^16.3.1",
"react-dom": "^16.3.1" "react-dom": "^16.3.1",
"rxjs": "^6.0.0-beta.4"
} }
} }
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
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 State from '../state'
import Observer from './observer';
export default class FileInput extends Observer<{ state: State } & WithStyles, { loading: boolean }> {
state = { loading: false }
componentDidMount() {
this.subscribe(this.props.state.loading, value => {
this.setState({ loading: value });
});
}
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()
}}
/>
}
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { Observable, Subscription } from 'rxjs';
export default class Observer<S, P> extends React.Component<S, P> {
private _subs: Subscription[] = []
subscribe<T>(obs: Observable<T>, onNext: (v: T) => void) {
this._subs.push(obs.subscribe(onNext));
}
componentWillUnmount() {
for (const s of this._subs) s.unsubscribe();
this._subs = [];
}
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import State from '../state'
export default class Viewport extends React.Component<{ state: State }, { initialized: boolean }> {
private canvasContainer: HTMLDivElement | null = null;
state = { initialized: false }
componentDidMount() {
if (this.canvasContainer) this.props.state.initRenderer(this.canvasContainer).then(() => this.setState({ initialized: true }))
}
render() {
return <div ref={elm => this.canvasContainer = elm} style={{ height: '100%' }}>
</div>
}
}
\ No newline at end of file
...@@ -4,127 +4,47 @@ ...@@ -4,127 +4,47 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import { ValueCell } from 'mol-util/value-cell' import { BehaviorSubject } from 'rxjs';
import { Vec3, Mat4 } from 'mol-math/linear-algebra' // import { ValueCell } from 'mol-util/value-cell'
import { createRenderer, createRenderObject } from 'mol-gl/renderer'
import { createColorTexture } from 'mol-gl/util'; // import { Vec3, Mat4 } from 'mol-math/linear-algebra'
import { createRenderer, Renderer } from 'mol-gl/renderer'
// import { createColorTexture } from 'mol-gl/util';
// import Icosahedron from 'mol-geo/primitive/icosahedron' // import Icosahedron from 'mol-geo/primitive/icosahedron'
// import Box from 'mol-geo/primitive/box' // import Box from 'mol-geo/primitive/box'
import Spacefill from 'mol-geo/representation/structure/spacefill' import Spacefill from 'mol-geo/representation/structure/spacefill'
import Point from 'mol-geo/representation/structure/point' import Point from 'mol-geo/representation/structure/point'
import CIF from 'mol-io/reader/cif' import { Run } from 'mol-task'
import { Run, Progress } from 'mol-task' import { Symmetry } from 'mol-model/structure'
import { Structure, Symmetry } from 'mol-model/structure'
function log(progress: Progress) {
const p = progress.root.progress
console.log(`${p.message} ${(p.current/p.max*100).toFixed(2)}%`)
}
async function parseCif(data: string|Uint8Array) {
const comp = CIF.parse(data)
const parsed = await Run(comp, log, 100);
if (parsed.isError) throw parsed;
return parsed
}
async function getPdb(pdb: string) {
const data = await fetch(`https://files.rcsb.org/download/${pdb}.cif`)
const parsed = await parseCif(await data.text())
const structure = Structure.ofData({ kind: 'mmCIF', data: CIF.schema.mmCIF(parsed.result.blocks[0]) })
return structure
}
import mcubes from './mcubes' // import mcubes from './utils/mcubes'
import { getStructuresFromPdbId } from './utils'
import { StructureRepresentation } from 'mol-geo/representation/structure'; import { StructureRepresentation } from 'mol-geo/representation/structure';
// import Cylinder from 'mol-geo/primitive/cylinder'; // import Cylinder from 'mol-geo/primitive/cylinder';
export default class State { export default class State {
renderer: Renderer
pdbId = '1crn'
initialized = new BehaviorSubject<boolean>(false)
loading = new BehaviorSubject<boolean>(false)
async initRenderer (container: HTMLDivElement) { async initRenderer (container: HTMLDivElement) {
const renderer = createRenderer(container) this.renderer = createRenderer(container)
this.initialized.next(true)
const p1 = Vec3.create(0, 4, 0) this.loadPdbId()
const p2 = Vec3.create(-3, 0, 0) this.renderer.frame()
// const position = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]))
// const normal = ValueCell.create(new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0]))
const transformArray1 = ValueCell.create(new Float32Array(16))
const transformArray2 = ValueCell.create(new Float32Array(16 * 3))
const m4 = Mat4.identity()
Mat4.toArray(m4, transformArray1.ref.value, 0)
Mat4.toArray(m4, transformArray2.ref.value, 0)
Mat4.setTranslation(m4, p1)
Mat4.toArray(m4, transformArray2.ref.value, 16)
Mat4.setTranslation(m4, p2)
Mat4.toArray(m4, transformArray2.ref.value, 32)
const color = ValueCell.create(createColorTexture(3))
color.ref.value.set([
0, 0, 255,
0, 255, 0,
255, 0, 0
])
// const points = createRenderObject('point', {
// position,
// transform: transformArray1
// })
// // renderer.add(points)
// const mesh = createRenderObject('mesh', {
// position,
// normal,
// color,
// transform: transformArray2
// })
// renderer.add(mesh)
// const cylinder = Cylinder({ height: 3, radiusBottom: 0.5, radiusTop: 0.5 })
// console.log(cylinder)
// const cylinderMesh = createRenderObject('mesh', {
// position: ValueCell.create(cylinder.vertices),
// normal: ValueCell.create(cylinder.normals),
// color,
// transform: transformArray2
// }, cylinder.indices)
// renderer.add(cylinderMesh)
// const sphere = Icosahedron()
// console.log(sphere)
// const box = Box()
// console.log(box)
// const points2 = createRenderObject('point', {
// position: ValueCell.create(new Float32Array(box.vertices)),
// transform: transformArray1
// })
// renderer.add(points2)
let rr = 0.7;
function cubesF(x: number, y: number, z: number) {
return x * x + y * y + z * z - rr * rr;
} }
let cubes = await mcubes(cubesF);
async loadPdbId () {
const makeCubesMesh = () => createRenderObject('mesh', { const { renderer, pdbId } = this
position: cubes.surface.vertexBuffer, renderer.clear()
normal: cubes.surface.normalBuffer,
color, if (pdbId.length !== 4) return
transform: transformArray2, this.loading.next(true)
elements: cubes.surface.indexBuffer,
const structures = await getStructuresFromPdbId(pdbId)
instanceCount: transformArray2.ref.value.length / 16,
elementCount: cubes.surface.triangleCount,
positionCount: cubes.surface.vertexCount
}, {});
const mesh2 = makeCubesMesh();
renderer.add(mesh2)
const structures = await getPdb('1rb8')
const struct = Symmetry.buildAssembly(structures[0], '1') const struct = Symmetry.buildAssembly(structures[0], '1')
const structPointRepr = StructureRepresentation(Point) const structPointRepr = StructureRepresentation(Point)
...@@ -135,6 +55,91 @@ export default class State { ...@@ -135,6 +55,91 @@ export default class State {
await Run(structSpacefillRepr.create(struct)) await Run(structSpacefillRepr.create(struct))
structSpacefillRepr.renderObjects.forEach(renderer.add) structSpacefillRepr.renderObjects.forEach(renderer.add)
renderer.frame() renderer.draw(true)
this.loading.next(false)
} }
} }
// async foo () {
// const p1 = Vec3.create(0, 4, 0)
// const p2 = Vec3.create(-3, 0, 0)
// // const position = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]))
// // const normal = ValueCell.create(new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0]))
// const transformArray1 = ValueCell.create(new Float32Array(16))
// const transformArray2 = ValueCell.create(new Float32Array(16 * 3))
// const m4 = Mat4.identity()
// Mat4.toArray(m4, transformArray1.ref.value, 0)
// Mat4.toArray(m4, transformArray2.ref.value, 0)
// Mat4.setTranslation(m4, p1)
// Mat4.toArray(m4, transformArray2.ref.value, 16)
// Mat4.setTranslation(m4, p2)
// Mat4.toArray(m4, transformArray2.ref.value, 32)
// const color = ValueCell.create(createColorTexture(3))
// color.ref.value.set([
// 0, 0, 255,
// 0, 255, 0,
// 255, 0, 0
// ])
// // const points = createRenderObject('point', {
// // position,
// // transform: transformArray1
// // })
// // // renderer.add(points)
// // const mesh = createRenderObject('mesh', {
// // position,
// // normal,
// // color,
// // transform: transformArray2
// // })
// // renderer.add(mesh)
// // const cylinder = Cylinder({ height: 3, radiusBottom: 0.5, radiusTop: 0.5 })
// // console.log(cylinder)
// // const cylinderMesh = createRenderObject('mesh', {
// // position: ValueCell.create(cylinder.vertices),
// // normal: ValueCell.create(cylinder.normals),
// // color,
// // transform: transformArray2
// // }, cylinder.indices)
// // renderer.add(cylinderMesh)
// // const sphere = Icosahedron()
// // console.log(sphere)
// // const box = Box()
// // console.log(box)
// // const points2 = createRenderObject('point', {
// // position: ValueCell.create(new Float32Array(box.vertices)),
// // transform: transformArray1
// // })
// // renderer.add(points2)
// // let rr = 0.7;
// // function cubesF(x: number, y: number, z: number) {
// // return x * x + y * y + z * z - rr * rr;
// // }
// // let cubes = await mcubes(cubesF);
// // const makeCubesMesh = () => createRenderObject('mesh', {
// // position: cubes.surface.vertexBuffer,
// // normal: cubes.surface.normalBuffer,
// // color,
// // transform: transformArray2,
// // elements: cubes.surface.indexBuffer,
// // instanceCount: transformArray2.ref.value.length / 16,
// // elementCount: cubes.surface.triangleCount,
// // positionCount: cubes.surface.vertexCount
// // }, {});
// // const mesh2 = makeCubesMesh();
// // renderer.add(mesh2)
// }
\ No newline at end of file
...@@ -5,18 +5,75 @@ ...@@ -5,18 +5,75 @@
*/ */
import * as React from 'react' import * as React from 'react'
import { withStyles, WithStyles, Theme, StyleRulesCallback } from 'material-ui/styles';
import Typography from 'material-ui/Typography';
import Toolbar from 'material-ui/Toolbar';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import Viewport from './components/viewport'
import FileInput from './components/file-input'
import State from './state' import State from './state'
export default class Root extends React.Component<{ state: State }, { initialized: boolean }> { const styles: StyleRulesCallback<any> = (theme: Theme) => ({
private canvasContainer: HTMLDivElement | null = null; root: {
state = { initialized: false } flexGrow: 1,
height: 830,
zIndex: 1,
overflow: 'hidden',
position: 'relative',
display: 'flex',
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
drawerPaper: {
position: 'relative',
width: 240,
},
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
minWidth: 0, // So the Typography noWrap works
},
toolbar: theme.mixins.toolbar,
textField: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
width: 200,
},
});
componentDidMount() { const decorate = withStyles(styles);
if (this.canvasContainer) this.props.state.initRenderer(this.canvasContainer).then(() => this.setState({ initialized: true }))
}
interface Props {
state: State;
};
class UI extends React.Component<{ state: State } & WithStyles, { }> {
render() { render() {
return <div ref={elm => this.canvasContainer = elm} style={{ position: 'absolute', top: 0, right: 0, left: 0, bottom: 0, overflow: 'hidden' }}> const { classes, state } = this.props;
return (
<div className={classes.root}>
<AppBar position='absolute' className={classes.appBar}>
<Toolbar>
<Typography variant='title' color='inherit' noWrap>
Mol* Render Test
</Typography>
</Toolbar>
</AppBar>
<Drawer variant='permanent' classes={{ paper: classes.drawerPaper, }}>
<div className={classes.toolbar} />
<FileInput state={state} classes={classes}></FileInput>
</Drawer>
<main className={classes.content}>
<div className={classes.toolbar} />
<Viewport state={state}></Viewport>
</main>
</div> </div>
);
} }
} }
export default decorate<Props>(UI)
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import CIF from 'mol-io/reader/cif'
import { Run, Progress } from 'mol-task'
import { Structure } from 'mol-model/structure'
export function log(progress: Progress) {
const p = progress.root.progress
console.log(`${p.message} ${(p.current/p.max*100).toFixed(2)}%`)
}
export async function parseCif(data: string|Uint8Array) {
const comp = CIF.parse(data)
const parsed = await Run(comp, log, 100);
if (parsed.isError) throw parsed;
return parsed
}
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]) })
}
\ No newline at end of file
...@@ -49,7 +49,7 @@ export interface Camera { ...@@ -49,7 +49,7 @@ export interface Camera {
update: (props: any, block: any) => void, update: (props: any, block: any) => void,
setState: (newState: CameraState) => void, setState: (newState: CameraState) => void,
getState: () => CameraState, getState: () => CameraState,
isDirty: () => boolean dirty: boolean
} }
export namespace Camera { export namespace Camera {
...@@ -185,7 +185,8 @@ export namespace Camera { ...@@ -185,7 +185,8 @@ export namespace Camera {
update, update,
setState, setState,
getState: () => Object.assign({}, state), getState: () => Object.assign({}, state),
isDirty: () => dirty get dirty() { return dirty },
set dirty(value: boolean) { dirty = value }
} }
} }
} }
...@@ -31,7 +31,6 @@ namespace Mesh { ...@@ -31,7 +31,6 @@ namespace Mesh {
} }
export function create(regl: REGL.Regl, data: Data, uniforms: Uniforms): Renderable { export function create(regl: REGL.Regl, data: Data, uniforms: Uniforms): Renderable {
console.log(data)
const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount))) const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount)))
const command = regl({ const command = regl({
...MeshShaders, ...MeshShaders,
......
...@@ -24,7 +24,6 @@ namespace Point { ...@@ -24,7 +24,6 @@ namespace Point {
} }
export function create(regl: REGL.Regl, data: Data): Renderable { export function create(regl: REGL.Regl, data: Data): Renderable {
console.log(data)
const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount))) const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount)))
const command = regl({ const command = regl({
...PointShaders, ...PointShaders,
......
...@@ -39,7 +39,8 @@ export function createRenderObject(type: 'mesh' | 'point', data: PointRenderable ...@@ -39,7 +39,8 @@ export function createRenderObject(type: 'mesh' | 'point', data: PointRenderable
export interface Renderer { export interface Renderer {
add: (o: RenderObject) => void add: (o: RenderObject) => void
remove: (o: RenderObject) => void remove: (o: RenderObject) => void
draw: () => void clear: () => void
draw: (force: boolean) => void
frame: () => void frame: () => void
} }
...@@ -54,18 +55,33 @@ export function createRenderer(container: HTMLDivElement): Renderer { ...@@ -54,18 +55,33 @@ export function createRenderer(container: HTMLDivElement): Renderer {
const renderableList: Renderable[] = [] const renderableList: Renderable[] = []
const objectIdRenderableMap: { [k: number]: Renderable } = {} const objectIdRenderableMap: { [k: number]: Renderable } = {}
const regl = glContext.create({ let regl: REGL.Regl
try {
regl = glContext.create({
container, container,
extensions: [ extensions: [
'OES_texture_float', 'OES_texture_float',
'OES_texture_float_linear', 'OES_texture_float_linear',
'OES_element_index_uint', 'OES_element_index_uint',
// 'EXT_disjoint_timer_query', 'EXT_disjoint_timer_query',
'EXT_blend_minmax', 'EXT_blend_minmax',
'ANGLE_instanced_arrays' 'ANGLE_instanced_arrays'
], ],
profile: true profile: true
}) })
} catch (e) {
regl = glContext.create({
container,
extensions: [
'OES_texture_float',
'OES_texture_float_linear',
'OES_element_index_uint',
'EXT_blend_minmax',
'ANGLE_instanced_arrays'
],
profile: true
})
}
const camera = Camera.create(regl, container, { const camera = Camera.create(regl, container, {
center: Vec3.create(0, 0, 0), center: Vec3.create(0, 0, 0),
...@@ -91,9 +107,9 @@ export function createRenderer(container: HTMLDivElement): Renderer { ...@@ -91,9 +107,9 @@ export function createRenderer(container: HTMLDivElement): Renderer {
} }
}) })
const draw = () => { const draw = (force = false) => {
camera.update((state: any) => { camera.update((state: any) => {
if (!camera.isDirty()) return; if (!force && !camera.dirty) return;
baseContext(() => { baseContext(() => {
// console.log(ctx) // console.log(ctx)
regl.clear({ color: [0, 0, 0, 1] }) regl.clear({ color: [0, 0, 0, 1] })
...@@ -118,6 +134,15 @@ export function createRenderer(container: HTMLDivElement): Renderer { ...@@ -118,6 +134,15 @@ export function createRenderer(container: HTMLDivElement): Renderer {
delete objectIdRenderableMap[o.id] delete objectIdRenderableMap[o.id]
} }
}, },
clear: () => {
for (const id in objectIdRenderableMap) {
// TODO
// objectIdRenderableMap[id].destroy()
delete objectIdRenderableMap[id]
}
renderableList.length = 0
camera.dirty = true
},
draw, draw,
frame: () => { frame: () => {
regl.frame((ctx) => draw()) regl.frame((ctx) => draw())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment