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 @@
"dependencies": {
"argparse": "^1.0.10",
"express": "^4.16.3",
"material-ui": "^1.0.0-beta.41",
"node-fetch": "^2.1.2",
"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
......@@ -10,4 +10,4 @@ import * as React from 'react'
import * as ReactDOM from 'react-dom'
const state = new State()
ReactDOM.render(<UI state={state} />, document.getElementById('app'));
\ No newline at end of file
ReactDOM.render(<UI state={ state } />, document.getElementById('app'));
\ No newline at end of file
......@@ -4,127 +4,47 @@
* @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 { createRenderer, createRenderObject } from 'mol-gl/renderer'
import { createColorTexture } from 'mol-gl/util';
// import { ValueCell } from 'mol-util/value-cell'
// 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 Box from 'mol-geo/primitive/box'
import Spacefill from 'mol-geo/representation/structure/spacefill'
import Point from 'mol-geo/representation/structure/point'
import CIF from 'mol-io/reader/cif'
import { Run, Progress } from 'mol-task'
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 { Run } from 'mol-task'
import { Symmetry } from 'mol-model/structure'
import mcubes from './mcubes'
// import mcubes from './utils/mcubes'
import { getStructuresFromPdbId } from './utils'
import { StructureRepresentation } from 'mol-geo/representation/structure';
// import Cylinder from 'mol-geo/primitive/cylinder';
export default class State {
renderer: Renderer
pdbId = '1crn'
initialized = new BehaviorSubject<boolean>(false)
loading = new BehaviorSubject<boolean>(false)
async initRenderer (container: HTMLDivElement) {
const renderer = createRenderer(container)
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)
const structures = await getPdb('1rb8')
this.renderer = createRenderer(container)
this.initialized.next(true)
this.loadPdbId()
this.renderer.frame()
}
async loadPdbId () {
const { renderer, pdbId } = this
renderer.clear()
if (pdbId.length !== 4) return
this.loading.next(true)
const structures = await getStructuresFromPdbId(pdbId)
const struct = Symmetry.buildAssembly(structures[0], '1')
const structPointRepr = StructureRepresentation(Point)
......@@ -135,6 +55,91 @@ export default class State {
await Run(structSpacefillRepr.create(struct))
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 @@
*/
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'
export default class Root extends React.Component<{ state: State }, { initialized: boolean }> {
private canvasContainer: HTMLDivElement | null = null;
state = { initialized: false }
const styles: StyleRulesCallback<any> = (theme: Theme) => ({
root: {
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() {
if (this.canvasContainer) this.props.state.initRenderer(this.canvasContainer).then(() => this.setState({ initialized: true }))
}
const decorate = withStyles(styles);
interface Props {
state: State;
};
class UI extends React.Component<{ state: State } & WithStyles, { }> {
render() {
return <div ref={elm => this.canvasContainer = elm} style={{ position: 'absolute', top: 0, right: 0, left: 0, bottom: 0, overflow: 'hidden' }}>
</div>
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>
);
}
}
\ No newline at end of file
}
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 {
update: (props: any, block: any) => void,
setState: (newState: CameraState) => void,
getState: () => CameraState,
isDirty: () => boolean
dirty: boolean
}
export namespace Camera {
......@@ -185,7 +185,8 @@ export namespace Camera {
update,
setState,
getState: () => Object.assign({}, state),
isDirty: () => dirty
get dirty() { return dirty },
set dirty(value: boolean) { dirty = value }
}
}
}
......@@ -31,7 +31,6 @@ namespace Mesh {
}
export function create(regl: REGL.Regl, data: Data, uniforms: Uniforms): Renderable {
console.log(data)
const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount)))
const command = regl({
...MeshShaders,
......
......@@ -24,7 +24,6 @@ namespace Point {
}
export function create(regl: REGL.Regl, data: Data): Renderable {
console.log(data)
const instanceId = ValueCell.create(fillSerial(new Float32Array(data.instanceCount)))
const command = regl({
...PointShaders,
......
......@@ -39,7 +39,8 @@ export function createRenderObject(type: 'mesh' | 'point', data: PointRenderable
export interface Renderer {
add: (o: RenderObject) => void
remove: (o: RenderObject) => void
draw: () => void
clear: () => void
draw: (force: boolean) => void
frame: () => void
}
......@@ -54,18 +55,33 @@ export function createRenderer(container: HTMLDivElement): Renderer {
const renderableList: Renderable[] = []
const objectIdRenderableMap: { [k: number]: Renderable } = {}
const regl = glContext.create({
container,
extensions: [
'OES_texture_float',
'OES_texture_float_linear',
'OES_element_index_uint',
// 'EXT_disjoint_timer_query',
'EXT_blend_minmax',
'ANGLE_instanced_arrays'
],
profile: true
})
let regl: REGL.Regl
try {
regl = glContext.create({
container,
extensions: [
'OES_texture_float',
'OES_texture_float_linear',
'OES_element_index_uint',
'EXT_disjoint_timer_query',
'EXT_blend_minmax',
'ANGLE_instanced_arrays'
],
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, {
center: Vec3.create(0, 0, 0),
......@@ -91,12 +107,12 @@ export function createRenderer(container: HTMLDivElement): Renderer {
}
})
const draw = () => {
const draw = (force = false) => {
camera.update((state: any) => {
if (!camera.isDirty()) return;
if (!force && !camera.dirty) return;
baseContext(() => {
// console.log(ctx)
regl.clear({color: [0, 0, 0, 1]})
regl.clear({ color: [0, 0, 0, 1] })
// TODO painters sort, filter visible, filter picking, visibility culling?
renderableList.forEach(r => {
r.draw()
......@@ -118,6 +134,15 @@ export function createRenderer(container: HTMLDivElement): Renderer {
delete objectIdRenderableMap[o.id]
}
},
clear: () => {
for (const id in objectIdRenderableMap) {
// TODO
// objectIdRenderableMap[id].destroy()
delete objectIdRenderableMap[id]
}
renderableList.length = 0
camera.dirty = true
},
draw,
frame: () => {
regl.frame((ctx) => draw())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment