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

volume representation, surface

parent 8f6a885c
Branches
Tags
No related merge requests found
Showing
with 293 additions and 101 deletions
...@@ -14,25 +14,25 @@ import Select from 'material-ui/Select'; ...@@ -14,25 +14,25 @@ import Select from 'material-ui/Select';
import State from '../state' import State from '../state'
import Observer from './observer'; import Observer from './observer';
interface DetailState { interface SphereDetailState {
loading: boolean loading: boolean
value: number value: number
} }
export default class Detail extends Observer<{ state: State } & WithStyles, DetailState> { export default class SphereDetail extends Observer<{ state: State } & WithStyles, SphereDetailState> {
state: DetailState = { loading: false, value: 2 } state: SphereDetailState = { loading: false, value: 2 }
componentDidMount() { componentDidMount() {
this.subscribe(this.props.state.loading, value => { this.subscribe(this.props.state.loading, value => {
this.setState({ loading: value }); this.setState({ loading: value });
}); });
this.subscribe(this.props.state.detail, value => { this.subscribe(this.props.state.sphereDetail, value => {
this.setState({ value }); this.setState({ value });
}); });
} }
handleValueChange = (event: React.ChangeEvent<any>) => { handleValueChange = (event: React.ChangeEvent<any>) => {
this.props.state.detail.next(event.target.value) this.props.state.sphereDetail.next(event.target.value)
} }
render() { render() {
...@@ -43,14 +43,14 @@ export default class Detail extends Observer<{ state: State } & WithStyles, Deta ...@@ -43,14 +43,14 @@ export default class Detail extends Observer<{ state: State } & WithStyles, Deta
}) })
return <FormControl className={classes.formControl}> return <FormControl className={classes.formControl}>
<InputLabel htmlFor='detail-value'>Detail</InputLabel> <InputLabel htmlFor='sphere-detail-value'>Sphere Detail</InputLabel>
<Select <Select
className={classes.selectField} className={classes.selectField}
value={this.state.value} value={this.state.value}
onChange={this.handleValueChange} onChange={this.handleValueChange}
inputProps={{ inputProps={{
name: 'value', name: 'value',
id: 'detail-value', id: 'sphere-detail-value',
}} }}
> >
{items} {items}
......
...@@ -20,9 +20,12 @@ import { Run } from 'mol-task' ...@@ -20,9 +20,12 @@ import { Run } from 'mol-task'
import { Symmetry, Structure, Model } from 'mol-model/structure' import { Symmetry, Structure, Model } from 'mol-model/structure'
// import mcubes from './utils/mcubes' // import mcubes from './utils/mcubes'
import { getModelFromPdbId, getModelFromFile, log } from './utils' import { getModelFromPdbId, getModelFromFile, log, Volume, getVolumeFromEmdId } from './utils'
import { StructureRepresentation } from 'mol-geo/representation/structure'; import { StructureRepresentation } from 'mol-geo/representation/structure';
import { Color } from 'mol-util/color'; import { Color } from 'mol-util/color';
import Surface, { SurfaceProps } from 'mol-geo/representation/volume/surface';
import { VolumeIsoValue } from 'mol-model/volume';
import { VolumeRepresentation } from 'mol-geo/representation/volume';
// import Cylinder from 'mol-geo/primitive/cylinder'; // import Cylinder from 'mol-geo/primitive/cylinder';
...@@ -37,14 +40,16 @@ export type ColorTheme = keyof typeof ColorTheme ...@@ -37,14 +40,16 @@ export type ColorTheme = keyof typeof ColorTheme
export default class State { export default class State {
viewer: Viewer viewer: Viewer
pdbId = '4cup' pdbId = ''
emdId = '8689'
model = new BehaviorSubject<Model | undefined>(undefined) model = new BehaviorSubject<Model | undefined>(undefined)
volume = new BehaviorSubject<Volume | undefined>(undefined)
initialized = new BehaviorSubject<boolean>(false) initialized = new BehaviorSubject<boolean>(false)
loading = new BehaviorSubject<boolean>(false) loading = new BehaviorSubject<boolean>(false)
colorTheme = new BehaviorSubject<ColorTheme>('element-symbol') colorTheme = new BehaviorSubject<ColorTheme>('element-symbol')
colorValue = new BehaviorSubject<Color>(0xFF4411) colorValue = new BehaviorSubject<Color>(0xFF4411)
detail = new BehaviorSubject<number>(0) sphereDetail = new BehaviorSubject<number>(0)
assembly = new BehaviorSubject<string>('') assembly = new BehaviorSubject<string>('')
pointVisibility = new BehaviorSubject<boolean>(true) pointVisibility = new BehaviorSubject<boolean>(true)
...@@ -52,11 +57,12 @@ export default class State { ...@@ -52,11 +57,12 @@ export default class State {
pointRepr: StructureRepresentation<PointProps> pointRepr: StructureRepresentation<PointProps>
spacefillRepr: StructureRepresentation<SpacefillProps> spacefillRepr: StructureRepresentation<SpacefillProps>
surfaceRepr: VolumeRepresentation<SurfaceProps>
constructor() { constructor() {
this.colorTheme.subscribe(() => this.update()) this.colorTheme.subscribe(() => this.update())
this.colorValue.subscribe(() => this.update()) this.colorValue.subscribe(() => this.update())
this.detail.subscribe(() => this.update()) this.sphereDetail.subscribe(() => this.update())
this.assembly.subscribe(() => this.initStructure()) this.assembly.subscribe(() => this.initStructure())
this.pointVisibility.subscribe(() => this.updateVisibility()) this.pointVisibility.subscribe(() => this.updateVisibility())
...@@ -66,7 +72,7 @@ export default class State { ...@@ -66,7 +72,7 @@ export default class State {
getSpacefillProps (): SpacefillProps { getSpacefillProps (): SpacefillProps {
const colorThemeName = this.colorTheme.getValue() const colorThemeName = this.colorTheme.getValue()
return { return {
detail: this.detail.getValue(), detail: this.sphereDetail.getValue(),
colorTheme: colorThemeName === 'uniform' ? colorTheme: colorThemeName === 'uniform' ?
{ name: colorThemeName, value: this.colorValue.getValue() } : { name: colorThemeName, value: this.colorValue.getValue() } :
{ name: colorThemeName } { name: colorThemeName }
...@@ -87,6 +93,7 @@ export default class State { ...@@ -87,6 +93,7 @@ export default class State {
this.viewer = Viewer.create(canvas, container) this.viewer = Viewer.create(canvas, container)
this.initialized.next(true) this.initialized.next(true)
this.loadPdbId() this.loadPdbId()
this.loadEmdId()
this.viewer.animate() this.viewer.animate()
} }
...@@ -97,7 +104,7 @@ export default class State { ...@@ -97,7 +104,7 @@ export default class State {
let structure: Structure let structure: Structure
const assemblies = model.symmetry.assemblies const assemblies = model.symmetry.assemblies
if (assemblies.length) { if (assemblies.length) {
structure = await Run(Symmetry.buildAssembly(Structure.ofModel(model), assembly || '1'), log, 100) structure = await Run(Symmetry.buildAssembly(Structure.ofModel(model), assembly || '1'), log, 500)
} else { } else {
structure = Structure.ofModel(model) structure = Structure.ofModel(model)
} }
...@@ -105,8 +112,8 @@ export default class State { ...@@ -105,8 +112,8 @@ export default class State {
} }
async initStructure () { async initStructure () {
const { viewer, model } = this const { viewer } = this
if (!viewer || !model) return if (!viewer || !this.model.getValue()) return
viewer.clear() viewer.clear()
...@@ -114,11 +121,11 @@ export default class State { ...@@ -114,11 +121,11 @@ export default class State {
if (!structure) return if (!structure) return
this.pointRepr = StructureRepresentation(Point) this.pointRepr = StructureRepresentation(Point)
await Run(this.pointRepr.create(structure, this.getPointProps()), log, 100) await Run(this.pointRepr.create(structure, this.getPointProps()), log, 500)
viewer.add(this.pointRepr) viewer.add(this.pointRepr)
this.spacefillRepr = StructureRepresentation(Spacefill) this.spacefillRepr = StructureRepresentation(Spacefill)
await Run(this.spacefillRepr.create(structure, this.getSpacefillProps()), log, 100) await Run(this.spacefillRepr.create(structure, this.getSpacefillProps()), log, 500)
viewer.add(this.spacefillRepr) viewer.add(this.spacefillRepr)
this.updateVisibility() this.updateVisibility()
...@@ -138,6 +145,21 @@ export default class State { ...@@ -138,6 +145,21 @@ export default class State {
this.setModel((await getModelFromFile(file))[0]) this.setModel((await getModelFromFile(file))[0])
} }
async initVolume () {
const { viewer } = this
const v = this.volume.getValue()
if (!viewer || !v) return
viewer.clear()
this.surfaceRepr = VolumeRepresentation(Surface)
await Run(this.surfaceRepr.create(v.volume, { isoValue: VolumeIsoValue.relative(v.volume.dataStats, 1.5) }), log, 500)
viewer.add(this.surfaceRepr)
viewer.requestDraw()
console.log(viewer.stats)
}
async loadPdbId () { async loadPdbId () {
this.viewer.clear() this.viewer.clear()
if (this.pdbId.length !== 4) return if (this.pdbId.length !== 4) return
...@@ -145,10 +167,23 @@ export default class State { ...@@ -145,10 +167,23 @@ export default class State {
this.setModel((await getModelFromPdbId(this.pdbId))[0]) this.setModel((await getModelFromPdbId(this.pdbId))[0])
} }
setVolume(volume: Volume) {
this.volume.next(volume)
this.initVolume()
this.loading.next(false)
}
async loadEmdId () {
this.viewer.clear()
if (this.emdId.length !== 4) return
this.loading.next(true)
this.setVolume(await getVolumeFromEmdId(this.emdId))
}
async update () { async update () {
if (!this.spacefillRepr) return if (!this.spacefillRepr) return
await Run(this.spacefillRepr.update(this.getSpacefillProps()), log, 100) await Run(this.spacefillRepr.update(this.getSpacefillProps()), log, 500)
await Run(this.pointRepr.update(this.getPointProps()), log, 100) await Run(this.pointRepr.update(this.getPointProps()), log, 500)
this.viewer.add(this.spacefillRepr) this.viewer.add(this.spacefillRepr)
this.viewer.add(this.pointRepr) this.viewer.add(this.pointRepr)
this.viewer.update() this.viewer.update()
......
...@@ -16,7 +16,7 @@ import State from './state' ...@@ -16,7 +16,7 @@ import State from './state'
import Viewport from './components/viewport' import Viewport from './components/viewport'
import FileInput from './components/file-input' import FileInput from './components/file-input'
import ColorTheme from './components/color-theme' import ColorTheme from './components/color-theme'
import Detail from './components/detail' import SphereDetail from './components/sphere-detail'
import Visibility from './components/visibility' import Visibility from './components/visibility'
import Assemblies from './components/assemblies' import Assemblies from './components/assemblies'
...@@ -86,7 +86,7 @@ class UI extends React.Component<{ state: State } & WithStyles, { }> { ...@@ -86,7 +86,7 @@ class UI extends React.Component<{ state: State } & WithStyles, { }> {
<div> <div>
<Assemblies state={state} classes={classes}></Assemblies> <Assemblies state={state} classes={classes}></Assemblies>
<ColorTheme state={state} classes={classes}></ColorTheme> <ColorTheme state={state} classes={classes}></ColorTheme>
<Detail state={state} classes={classes}></Detail> <SphereDetail state={state} classes={classes}></SphereDetail>
<Visibility state={state} classes={classes}></Visibility> <Visibility state={state} classes={classes}></Visibility>
</div> </div>
</form> </form>
......
...@@ -7,23 +7,29 @@ ...@@ -7,23 +7,29 @@
import CIF from 'mol-io/reader/cif' import CIF from 'mol-io/reader/cif'
import { Run, Progress } from 'mol-task' import { Run, Progress } from 'mol-task'
import { Model } from 'mol-model/structure' import { Model } from 'mol-model/structure'
import { VolumeData, parseDensityServerData } from 'mol-model/volume'
import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server';
export function log(progress: Progress) { export function log(progress: Progress) {
const p = progress.root.progress const p = progress.root.progress
console.log(`${p.message} ${(p.current/p.max*100).toFixed(2)}%`) console.log(`${p.message} ${(p.current/p.max*100).toFixed(2)}%`)
} }
export async function downloadCif(url: string, isBinary: boolean) {
const data = await fetch(url);
return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
}
export async function parseCif(data: string|Uint8Array) { export async function parseCif(data: string|Uint8Array) {
const comp = CIF.parse(data) const comp = CIF.parse(data)
const parsed = await Run(comp, log, 100); const parsed = await Run(comp, log, 500);
if (parsed.isError) throw parsed; if (parsed.isError) throw parsed;
return parsed return parsed.result
} }
export async function getModelFromPdbId(pdbid: string) { export async function getModelFromPdbId(pdbid: string) {
const data = await fetch(`https://files.rcsb.org/download/${pdbid}.cif`) const cif = await downloadCif(`https://files.rcsb.org/download/${pdbid}.cif`, false)
const parsed = await parseCif(await data.text()) return Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(cif.blocks[0]) })
return Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(parsed.result.blocks[0]) })
} }
const readFileAsText = (file: File) => { const readFileAsText = (file: File) => {
...@@ -39,6 +45,14 @@ const readFileAsText = (file: File) => { ...@@ -39,6 +45,14 @@ const readFileAsText = (file: File) => {
} }
export async function getModelFromFile(file: File) { export async function getModelFromFile(file: File) {
const parsed = await parseCif(await readFileAsText(file)) const cif = await parseCif(await readFileAsText(file))
return Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(parsed.result.blocks[0]) }) return Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(cif.blocks[0]) })
}
export type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
export async function getVolumeFromEmdId(emdid: string): Promise<Volume> {
const cif = await downloadCif(`https://webchem.ncbr.muni.cz/DensityServer/em/emd-${emdid}/cell?detail=4`, true)
const data = CIF.schema.densityServer(cif.blocks[1])
return { source: data, volume: await Run(parseDensityServerData(data)) }
} }
\ No newline at end of file
...@@ -14,7 +14,7 @@ import CIF from 'mol-io/reader/cif' ...@@ -14,7 +14,7 @@ import CIF from 'mol-io/reader/cif'
import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server'; import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server';
import { Run } from 'mol-task'; import { Run } from 'mol-task';
import { Table } from 'mol-data/db'; import { Table } from 'mol-data/db';
import { computeVolumeMesh } from 'mol-geo/representation/volume/Mesh'; import { computeVolumeSurface } from 'mol-geo/representation/volume/surface';
import { StringBuilder } from 'mol-util'; import { StringBuilder } from 'mol-util';
require('util.promisify').shim(); require('util.promisify').shim();
...@@ -38,7 +38,7 @@ function print(data: Volume) { ...@@ -38,7 +38,7 @@ function print(data: Volume) {
} }
async function doMesh(data: Volume, filename: string) { async function doMesh(data: Volume, filename: string) {
const mesh = await Run(computeVolumeMesh(data.volume, VolumeIsoValue.relative(data.volume.dataStats, 1.5))); const mesh = await Run(computeVolumeSurface(data.volume, VolumeIsoValue.relative(data.volume.dataStats, 1.5)));
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount }); console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
// Export the mesh in OBJ format. // Export the mesh in OBJ format.
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Task } from 'mol-task'
import { RenderObject } from 'mol-gl/scene';
export interface RepresentationProps {}
export interface Representation<D, P extends RepresentationProps = {}> {
renderObjects: ReadonlyArray<RenderObject>
create: (data: D, props?: P) => Task<void>
update: (props: P) => Task<void>
}
\ No newline at end of file
...@@ -9,22 +9,20 @@ import { EquivalenceClasses } from 'mol-data/util'; ...@@ -9,22 +9,20 @@ import { EquivalenceClasses } from 'mol-data/util';
import { OrderedSet } from 'mol-data/int' import { OrderedSet } from 'mol-data/int'
import { Task } from 'mol-task' import { Task } from 'mol-task'
import { RenderObject } from 'mol-gl/scene'; import { RenderObject } from 'mol-gl/scene';
import { Representation, RepresentationProps } from '..';
// import { Mat4, EPSILON } from 'mol-math/linear-algebra'; // import { Mat4, EPSILON } from 'mol-math/linear-algebra';
export interface RepresentationProps {
} export interface UnitsRepresentation<P> {
export interface UnitsRepresentation<Props = {}> {
renderObjects: ReadonlyArray<RenderObject> renderObjects: ReadonlyArray<RenderObject>
create: (units: ReadonlyArray<Unit>, elementGroup: ElementGroup, props: Props) => Task<void> create: (units: ReadonlyArray<Unit>, elementGroup: ElementGroup, props: P) => Task<void>
update: (props: RepresentationProps) => Task<boolean> update: (props: P) => Task<boolean>
} }
export interface StructureRepresentation<Props = {}> { export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> {
renderObjects: ReadonlyArray<RenderObject> renderObjects: ReadonlyArray<RenderObject>
create: (structure: Structure, props?: Props) => Task<void> create: (structure: Structure, props?: P) => Task<void>
update: (props: Props) => Task<void> update: (props: P) => Task<void>
} }
interface GroupRepresentation<T> { interface GroupRepresentation<T> {
...@@ -33,13 +31,13 @@ interface GroupRepresentation<T> { ...@@ -33,13 +31,13 @@ interface GroupRepresentation<T> {
elementGroup: ElementGroup elementGroup: ElementGroup
} }
export function StructureRepresentation<Props>(reprCtor: () => UnitsRepresentation<Props>): StructureRepresentation<Props> { export function StructureRepresentation<P>(reprCtor: () => UnitsRepresentation<P>): StructureRepresentation<P> {
const renderObjects: RenderObject[] = [] const renderObjects: RenderObject[] = []
const groupReprs: GroupRepresentation<Props>[] = [] const groupReprs: GroupRepresentation<P>[] = []
return { return {
renderObjects, renderObjects,
create(structure: Structure, props: Props = {} as Props) { create(structure: Structure, props: P = {} as P) {
return Task.create('StructureRepresentation.create', async ctx => { return Task.create('StructureRepresentation.create', async ctx => {
const { elements, units } = structure; const { elements, units } = structure;
const uniqueGroups = EquivalenceClasses<number, { unit: Unit, group: ElementGroup }>( const uniqueGroups = EquivalenceClasses<number, { unit: Unit, group: ElementGroup }>(
...@@ -73,20 +71,20 @@ export function StructureRepresentation<Props>(reprCtor: () => UnitsRepresentati ...@@ -73,20 +71,20 @@ export function StructureRepresentation<Props>(reprCtor: () => UnitsRepresentati
const elementGroup = ElementSet.groupFromUnitIndex(elements, group[0]) const elementGroup = ElementSet.groupFromUnitIndex(elements, group[0])
const repr = reprCtor() const repr = reprCtor()
groupReprs.push({ repr, units: groupUnits, elementGroup }) groupReprs.push({ repr, units: groupUnits, elementGroup })
await ctx.update({ message: 'Building units...', current: i, max: il }); await ctx.update({ message: 'Building structure unit representations...', current: i, max: il });
await ctx.runChild(repr.create(groupUnits, elementGroup, props)); await ctx.runChild(repr.create(groupUnits, elementGroup, props));
renderObjects.push(...repr.renderObjects) renderObjects.push(...repr.renderObjects)
} }
}); });
}, },
update(props: Props) { update(props: P) {
return Task.create('StructureRepresentation.update', async ctx => { return Task.create('StructureRepresentation.update', async ctx => {
// TODO check model.id, conformation.id, unit.id, elementGroup(.hashCode/.areEqual) // TODO check model.id, conformation.id, unit.id, elementGroup(.hashCode/.areEqual)
renderObjects.length = 0 // clear renderObjects.length = 0 // clear
for (let i = 0, il = groupReprs.length; i < il; ++i) { for (let i = 0, il = groupReprs.length; i < il; ++i) {
const groupRepr = groupReprs[i] const groupRepr = groupReprs[i]
const { repr, units, elementGroup } = groupRepr const { repr, units, elementGroup } = groupRepr
await ctx.update({ message: 'Updating units...', current: i, max: il }); await ctx.update({ message: 'Updating structure unit representations...', current: i, max: il });
if (!await ctx.runChild(repr.update(props))) { if (!await ctx.runChild(repr.update(props))) {
await ctx.runChild(repr.create(units, elementGroup, props)) await ctx.runChild(repr.create(units, elementGroup, props))
} }
......
...@@ -11,7 +11,7 @@ import { Unit, ElementGroup, Element } from 'mol-model/structure'; ...@@ -11,7 +11,7 @@ import { Unit, ElementGroup, Element } from 'mol-model/structure';
import { Task } from 'mol-task' import { Task } from 'mol-task'
import { fillSerial } from 'mol-gl/renderable/util'; import { fillSerial } from 'mol-gl/renderable/util';
import { RepresentationProps, UnitsRepresentation } from './index'; import { UnitsRepresentation } from './index';
import VertexMap from '../../shape/vertex-map'; import VertexMap from '../../shape/vertex-map';
import { ColorTheme, SizeTheme } from '../../theme'; import { ColorTheme, SizeTheme } from '../../theme';
import { createTransforms, createColors, createSizes } from './utils'; import { createTransforms, createColors, createSizes } from './utils';
...@@ -100,7 +100,7 @@ export default function Point(): UnitsRepresentation<PointProps> { ...@@ -100,7 +100,7 @@ export default function Point(): UnitsRepresentation<PointProps> {
renderObjects.push(points) renderObjects.push(points)
}) })
}, },
update(props: RepresentationProps) { update(props: PointProps) {
return Task.create('Point.update', async ctx => { return Task.create('Point.update', async ctx => {
if (!points || !_units || !_elementGroup) return false if (!points || !_units || !_elementGroup) return false
......
...@@ -11,7 +11,7 @@ import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/s ...@@ -11,7 +11,7 @@ import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/s
import { Vec3, Mat4 } from 'mol-math/linear-algebra' import { Vec3, Mat4 } from 'mol-math/linear-algebra'
import { OrderedSet } from 'mol-data/int' import { OrderedSet } from 'mol-data/int'
import { Unit, ElementGroup, Element, Queries } from 'mol-model/structure'; import { Unit, ElementGroup, Element, Queries } from 'mol-model/structure';
import { RepresentationProps, UnitsRepresentation } from './index'; import { UnitsRepresentation } from './index';
import { Task } from 'mol-task' import { Task } from 'mol-task'
import { MeshBuilder } from '../../shape/mesh-builder'; import { MeshBuilder } from '../../shape/mesh-builder';
import { createTransforms, createColors } from './utils'; import { createTransforms, createColors } from './utils';
...@@ -110,7 +110,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> { ...@@ -110,7 +110,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
renderObjects.push(spheres) renderObjects.push(spheres)
}) })
}, },
update(props: RepresentationProps) { update(props: SpacefillProps) {
return Task.create('Spacefill.update', async ctx => { return Task.create('Spacefill.update', async ctx => {
if (!spheres) return false if (!spheres) return false
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { VolumeData, VolumeIsoValue } from 'mol-model/volume'
import { Task } from 'mol-task'
import { Mesh } from '../../shape/mesh';
import { computeMarchingCubes } from '../../util/marching-cubes/algorithm';
function computeVolumeMesh(volume: VolumeData, isoValue: VolumeIsoValue) {
return Task.create<Mesh>('Volume Surface', async ctx => {
ctx.update({ message: 'Marching cubes...' });
const mesh = await ctx.runChild(computeMarchingCubes({
isoLevel: VolumeIsoValue.toAbsolute(isoValue).absoluteValue,
scalarField: volume.data
}));
const transform = VolumeData.getGridToCartesianTransform(volume);
ctx.update({ message: 'Transforming mesh...' });
Mesh.transformImmediate(mesh, transform);
return mesh;
});
}
export { computeVolumeMesh }
\ 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 { Task } from 'mol-task'
import { RenderObject } from 'mol-gl/scene';
import { RepresentationProps, Representation } from '..';
import { VolumeData } from 'mol-model/volume';
export interface VolumeElementRepresentation<P> {
renderObjects: ReadonlyArray<RenderObject>
create: (volumeData: VolumeData, props: P) => Task<void>
update: (props: P) => Task<boolean>
}
export interface VolumeRepresentation<P extends RepresentationProps = {}> extends Representation<VolumeData, P> {
renderObjects: ReadonlyArray<RenderObject>
create: (volumeData: VolumeData, props?: P) => Task<void>
update: (props: P) => Task<void>
}
export function VolumeRepresentation<P>(reprCtor: () => VolumeElementRepresentation<P>): VolumeRepresentation<P> {
const renderObjects: RenderObject[] = []
return {
renderObjects,
create(volumeData: VolumeData, props: P = {} as P) {
return Task.create('VolumeRepresentation.create', async ctx => {
const repr = reprCtor()
await ctx.update({ message: 'Building volume representation...', current: 0, max: 1 });
await ctx.runChild(repr.create(volumeData, props));
renderObjects.push(...repr.renderObjects)
});
},
update(props: P) {
return Task.create('VolumeRepresentation.update', async ctx => {})
}
}
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { VolumeData, VolumeIsoValue } from 'mol-model/volume'
import { Task } from 'mol-task'
import { computeMarchingCubes } from '../../util/marching-cubes/algorithm';
import { Mesh } from '../../shape/mesh';
import { VolumeElementRepresentation } from '.';
import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/scene';
import { fillSerial } from 'mol-gl/renderable/util';
import { ValueCell } from 'mol-util';
import { Mat4 } from 'mol-math/linear-algebra';
import { createUniformColor } from '../../util/color-data';
export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValue) {
return Task.create<Mesh>('Volume Surface', async ctx => {
ctx.update({ message: 'Marching cubes...' });
const mesh = await ctx.runChild(computeMarchingCubes({
isoLevel: VolumeIsoValue.toAbsolute(isoValue).absoluteValue,
scalarField: volume.data
}));
const transform = VolumeData.getGridToCartesianTransform(volume);
ctx.update({ message: 'Transforming mesh...' });
Mesh.transformImmediate(mesh, transform);
return mesh;
});
}
export const DefaultSurfaceProps = {
isoValue: VolumeIsoValue.relative({ min: 0, max: 0, mean: 0, sigma: 0 }, 0)
}
export type SurfaceProps = Partial<typeof DefaultSurfaceProps>
export default function Surface(): VolumeElementRepresentation<SurfaceProps> {
const renderObjects: RenderObject[] = []
let surface: MeshRenderObject
let curProps = DefaultSurfaceProps
return {
renderObjects,
create(volume: VolumeData, props: SurfaceProps = {}) {
return Task.create('Point.create', async ctx => {
renderObjects.length = 0 // clear
curProps = { ...DefaultSurfaceProps, ...props }
const mesh = await ctx.runChild(computeVolumeSurface(volume, curProps.isoValue))
surface = createMeshRenderObject({
objectId: 0,
position: mesh.vertexBuffer,
normal: mesh.normalBuffer,
id: ValueCell.create(fillSerial(new Float32Array(mesh.vertexCount / 3))),
color: createUniformColor({ value: 0x999999 }),
transform: ValueCell.create(new Float32Array(Mat4.identity())),
index: mesh.indexBuffer,
instanceCount: 1,
indexCount: mesh.triangleCount,
elementCount: mesh.triangleCount,
positionCount: mesh.vertexCount / 3,
flatShaded: true
})
renderObjects.push(surface)
})
},
update(props: SurfaceProps) {
return Task.create('Surface.update', async ctx => {
// TODO
return false
})
}
}
}
...@@ -20,7 +20,7 @@ namespace Mesh { ...@@ -20,7 +20,7 @@ namespace Mesh {
objectId: number objectId: number
position: ValueCell<Float32Array> position: ValueCell<Float32Array>
normal?: ValueCell<Float32Array> normal: ValueCell<Float32Array | undefined>
id: ValueCell<Float32Array> id: ValueCell<Float32Array>
color: ColorData color: ColorData
...@@ -31,12 +31,19 @@ namespace Mesh { ...@@ -31,12 +31,19 @@ namespace Mesh {
instanceCount: number instanceCount: number
elementCount: number elementCount: number
positionCount: number positionCount: number
flatShaded?: boolean
doubleSided?: boolean
} }
export function create(ctx: Context, props: Props): Renderable<Props> { export function create(ctx: Context, props: Props): Renderable<Props> {
const defines = getBaseDefines(props)
if (props.flatShaded) defines.FLAT_SHADED = ''
if (props.doubleSided) defines.DOUBLE_SIDED = ''
const defs: RenderItemProps = { const defs: RenderItemProps = {
...getBaseDefs(props), ...getBaseDefs(props),
shaderCode: addShaderDefines(getBaseDefines(props), MeshShaderCode), shaderCode: addShaderDefines(defines, MeshShaderCode),
drawMode: 'triangles', drawMode: 'triangles',
elementsKind: 'uint32' elementsKind: 'uint32'
} }
......
...@@ -67,7 +67,7 @@ interface BaseProps { ...@@ -67,7 +67,7 @@ interface BaseProps {
positionCount: number, positionCount: number,
position: ValueCell<Float32Array> position: ValueCell<Float32Array>
normal?: ValueCell<Float32Array> normal?: ValueCell<Float32Array | undefined>
id: ValueCell<Float32Array> id: ValueCell<Float32Array>
transform: ValueCell<Float32Array> transform: ValueCell<Float32Array>
...@@ -133,7 +133,7 @@ export function getBaseAttributeDefs(props: BaseProps) { ...@@ -133,7 +133,7 @@ export function getBaseAttributeDefs(props: BaseProps) {
elementId: { kind: 'float32', itemSize: 1, divisor: 0 }, elementId: { kind: 'float32', itemSize: 1, divisor: 0 },
transform: { kind: 'float32', itemSize: 16, divisor: 1 }, transform: { kind: 'float32', itemSize: 16, divisor: 1 },
} }
if (props.normal) { if (props.normal && props.normal.ref.value) {
attributeDefs.normal = { kind: 'float32', itemSize: 3, divisor: 0 } attributeDefs.normal = { kind: 'float32', itemSize: 3, divisor: 0 }
} }
const color = props.color const color = props.color
...@@ -156,7 +156,7 @@ export function getBaseAttributeValues(props: BaseProps) { ...@@ -156,7 +156,7 @@ export function getBaseAttributeValues(props: BaseProps) {
elementId: id.ref.value, elementId: id.ref.value,
transform: transform.ref.value transform: transform.ref.value
} }
if (normal) { if (normal && normal.ref.value) {
attributeValues.normal = normal.ref.value attributeValues.normal = normal.ref.value
} }
const color = props.color const color = props.color
......
...@@ -23,7 +23,8 @@ export const MeshShaderCode: ShaderCode = { ...@@ -23,7 +23,8 @@ export const MeshShaderCode: ShaderCode = {
type ShaderDefine = ( type ShaderDefine = (
'UNIFORM_COLOR' | 'ATTRIBUTE_COLOR' | 'INSTANCE_COLOR' | 'ELEMENT_COLOR' | 'ELEMENT_INSTANCE_COLOR' | 'UNIFORM_COLOR' | 'ATTRIBUTE_COLOR' | 'INSTANCE_COLOR' | 'ELEMENT_COLOR' | 'ELEMENT_INSTANCE_COLOR' |
'UNIFORM_SIZE' | 'ATTRIBUTE_SIZE' | 'UNIFORM_SIZE' | 'ATTRIBUTE_SIZE' |
'POINT_SIZE_ATTENUATION' 'POINT_SIZE_ATTENUATION' |
'FLAT_SHADED' | 'DOUBLE_SIDED'
) )
export type ShaderDefines = { export type ShaderDefines = {
[k in ShaderDefine]?: number|string [k in ShaderDefine]?: number|string
......
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
#ifdef FLAT_SHADED
#extension GL_OES_standard_derivatives : enable
#endif
precision highp float; precision highp float;
// uniform vec3 light_position; // uniform vec3 light_position;
...@@ -11,7 +15,10 @@ uniform vec3 light_color; ...@@ -11,7 +15,10 @@ uniform vec3 light_color;
uniform vec3 light_ambient; uniform vec3 light_ambient;
uniform mat4 view; uniform mat4 view;
varying vec3 vNormal, vViewPosition; #ifndef FLAT_SHADED
varying vec3 vNormal;
#endif
varying vec3 vViewPosition;
#pragma glslify: import('./chunks/color-frag-params.glsl') #pragma glslify: import('./chunks/color-frag-params.glsl')
...@@ -35,7 +42,18 @@ void main() { ...@@ -35,7 +42,18 @@ void main() {
vec3 L = normalize(lightVector); // light direction vec3 L = normalize(lightVector); // light direction
vec3 V = normalize(vViewPosition); // eye direction vec3 V = normalize(vViewPosition); // eye direction
vec3 N = normalize(-vNormal); // surface normal
// surface normal
#ifdef FLAT_SHADED
vec3 fdx = dFdx(vViewPosition);
vec3 fdy = dFdy(vViewPosition);
vec3 N = -normalize(cross(fdx, fdy));
#else
vec3 N = -normalize(vNormal);
#ifdef DOUBLE_SIDED
N = N * (float(gl_FrontFacing) * 2.0 - 1.0);
#endif
#endif
// compute our diffuse & specular terms // compute our diffuse & specular terms
float specular = calculateSpecular(L, V, N, shininess) * specularScale; float specular = calculateSpecular(L, V, N, shininess) * specularScale;
......
...@@ -19,9 +19,11 @@ attribute mat4 transform; ...@@ -19,9 +19,11 @@ attribute mat4 transform;
attribute float instanceId; attribute float instanceId;
attribute float elementId; attribute float elementId;
#ifndef FLAT_SHADED
attribute vec3 normal; attribute vec3 normal;
varying vec3 vNormal; varying vec3 vNormal;
#endif
varying vec3 vViewPosition; varying vec3 vViewPosition;
#pragma glslify: inverse = require(./utils/inverse.glsl) #pragma glslify: inverse = require(./utils/inverse.glsl)
...@@ -35,6 +37,8 @@ void main(){ ...@@ -35,6 +37,8 @@ void main(){
vViewPosition = mvPosition.xyz; vViewPosition = mvPosition.xyz;
gl_Position = projection * mvPosition; gl_Position = projection * mvPosition;
#ifndef FLAT_SHADED
mat3 normalMatrix = transpose(inverse(mat3(modelView))); mat3 normalMatrix = transpose(inverse(mat3(modelView)));
vNormal = normalize(normalMatrix * normal); vNormal = normalize(normalMatrix * normal);
#endif
} }
\ No newline at end of file
...@@ -33,6 +33,7 @@ function unbindResources (gl: WebGLRenderingContext) { ...@@ -33,6 +33,7 @@ function unbindResources (gl: WebGLRenderingContext) {
type Extensions = { type Extensions = {
angleInstancedArrays: ANGLE_instanced_arrays angleInstancedArrays: ANGLE_instanced_arrays
standardDerivatives: OES_standard_derivatives
oesElementIndexUint: OES_element_index_uint | null oesElementIndexUint: OES_element_index_uint | null
oesVertexArrayObject: OES_vertex_array_object | null oesVertexArrayObject: OES_vertex_array_object | null
} }
...@@ -53,6 +54,10 @@ export function createContext(gl: WebGLRenderingContext): Context { ...@@ -53,6 +54,10 @@ export function createContext(gl: WebGLRenderingContext): Context {
if (angleInstancedArrays === null) { if (angleInstancedArrays === null) {
throw new Error('Could not get "ANGLE_instanced_arrays" extension') throw new Error('Could not get "ANGLE_instanced_arrays" extension')
} }
const standardDerivatives = gl.getExtension('OES_standard_derivatives')
if (standardDerivatives === null) {
throw new Error('Could not get "OES_standard_derivatives" extension')
}
const oesElementIndexUint = gl.getExtension('OES_element_index_uint') const oesElementIndexUint = gl.getExtension('OES_element_index_uint')
if (oesElementIndexUint === null) { if (oesElementIndexUint === null) {
console.warn('Could not get "OES_element_index_uint" extension') console.warn('Could not get "OES_element_index_uint" extension')
...@@ -67,7 +72,7 @@ export function createContext(gl: WebGLRenderingContext): Context { ...@@ -67,7 +72,7 @@ export function createContext(gl: WebGLRenderingContext): Context {
return { return {
gl, gl,
extensions: { angleInstancedArrays, oesElementIndexUint, oesVertexArrayObject }, extensions: { angleInstancedArrays, standardDerivatives, oesElementIndexUint, oesVertexArrayObject },
shaderCache, shaderCache,
programCache, programCache,
bufferCount: 0, bufferCount: 0,
......
...@@ -9,20 +9,20 @@ import InputObserver from 'mol-util/input/input-observer' ...@@ -9,20 +9,20 @@ import InputObserver from 'mol-util/input/input-observer'
import * as SetUtils from 'mol-util/set' import * as SetUtils from 'mol-util/set'
import Renderer, { RendererStats } from 'mol-gl/renderer' import Renderer, { RendererStats } from 'mol-gl/renderer'
import { RenderObject } from 'mol-gl/scene' import { RenderObject } from 'mol-gl/scene'
import { StructureRepresentation } from 'mol-geo/representation/structure';
import TrackballControls from './controls/trackball' import TrackballControls from './controls/trackball'
import { Viewport } from './camera/util' import { Viewport } from './camera/util'
import { PerspectiveCamera } from './camera/perspective' import { PerspectiveCamera } from './camera/perspective'
import { resizeCanvas } from './util'; import { resizeCanvas } from './util';
import { createContext } from 'mol-gl/webgl/context'; import { createContext } from 'mol-gl/webgl/context';
import { Representation } from 'mol-geo/representation';
interface Viewer { interface Viewer {
hide: (repr: StructureRepresentation) => void hide: (repr: Representation<any>) => void
show: (repr: StructureRepresentation) => void show: (repr: Representation<any>) => void
add: (repr: StructureRepresentation) => void add: (repr: Representation<any>) => void
remove: (repr: StructureRepresentation) => void remove: (repr: Representation<any>) => void
update: () => void update: () => void
clear: () => void clear: () => void
...@@ -49,7 +49,7 @@ function getWebGLContext(canvas: HTMLCanvasElement, contextAttributes?: WebGLCon ...@@ -49,7 +49,7 @@ function getWebGLContext(canvas: HTMLCanvasElement, contextAttributes?: WebGLCon
namespace Viewer { namespace Viewer {
export function create(canvas: HTMLCanvasElement, container: Element): Viewer { export function create(canvas: HTMLCanvasElement, container: Element): Viewer {
const reprMap = new Map<StructureRepresentation, Set<RenderObject>>() const reprMap = new Map<Representation<any>, Set<RenderObject>>()
const input = InputObserver.create(canvas) const input = InputObserver.create(canvas)
input.resize.subscribe(handleResize) input.resize.subscribe(handleResize)
...@@ -99,16 +99,16 @@ namespace Viewer { ...@@ -99,16 +99,16 @@ namespace Viewer {
handleResize() handleResize()
return { return {
hide: (repr: StructureRepresentation) => { hide: (repr: Representation<any>) => {
const renderObjectSet = reprMap.get(repr) const renderObjectSet = reprMap.get(repr)
if (renderObjectSet) renderObjectSet.forEach(o => o.visible = false) if (renderObjectSet) renderObjectSet.forEach(o => o.visible = false)
}, },
show: (repr: StructureRepresentation) => { show: (repr: Representation<any>) => {
const renderObjectSet = reprMap.get(repr) const renderObjectSet = reprMap.get(repr)
if (renderObjectSet) renderObjectSet.forEach(o => o.visible = true) if (renderObjectSet) renderObjectSet.forEach(o => o.visible = true)
}, },
add: (repr: StructureRepresentation) => { add: (repr: Representation<any>) => {
const oldRO = reprMap.get(repr) const oldRO = reprMap.get(repr)
const newRO = new Set<RenderObject>() const newRO = new Set<RenderObject>()
repr.renderObjects.forEach(o => newRO.add(o)) repr.renderObjects.forEach(o => newRO.add(o))
...@@ -120,7 +120,7 @@ namespace Viewer { ...@@ -120,7 +120,7 @@ namespace Viewer {
} }
reprMap.set(repr, newRO) reprMap.set(repr, newRO)
}, },
remove: (repr: StructureRepresentation) => { remove: (repr: Representation<any>) => {
const renderObjectSet = reprMap.get(repr) const renderObjectSet = reprMap.get(repr)
if (renderObjectSet) renderObjectSet.forEach(o => renderer.remove(o)) if (renderObjectSet) renderObjectSet.forEach(o => renderer.remove(o))
}, },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment