From 03dbb0674c50cfe4cd0bc1d6b07c4624556eac1c Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Wed, 5 Sep 2018 17:10:47 -0700 Subject: [PATCH] wip, canvas example --- .../component/structure-representation.tsx | 30 ++++-- src/apps/canvas/component/structure-view.tsx | 65 +++++++++--- src/apps/canvas/index.html | 20 ++-- src/apps/canvas/structure-view.ts | 99 +++++++++++++------ 4 files changed, 157 insertions(+), 57 deletions(-) diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx index 06f2b7725..3a08a80ca 100644 --- a/src/apps/canvas/component/structure-representation.tsx +++ b/src/apps/canvas/component/structure-representation.tsx @@ -7,6 +7,7 @@ import * as React from 'react' import { StructureRepresentation, StructureProps } from 'mol-geo/representation/structure'; import Viewer from 'mol-view/viewer'; +import { VisualQuality } from 'mol-geo/representation/util'; export interface StructureRepresentationComponentProps { viewer: Viewer @@ -16,12 +17,14 @@ export interface StructureRepresentationComponentProps { export interface StructureRepresentationComponentState { label: string visible: boolean + quality: VisualQuality } export class StructureRepresentationComponent extends React.Component<StructureRepresentationComponentProps, StructureRepresentationComponentState> { state = { label: this.props.representation.label, visible: this.props.representation.props.visible, + quality: this.props.representation.props.quality, } componentWillMount() { @@ -31,27 +34,31 @@ export class StructureRepresentationComponent extends React.Component<StructureR ...this.state, label: repr.label, visible: repr.props.visible, + quality: repr.props.quality, }) } async update(state: Partial<StructureRepresentationComponentState>) { const repr = this.props.representation + const props: Partial<StructureProps> = {} - if (state.visible !== undefined) { - await repr.createOrUpdate({ visible: state.visible }).run() - // this.props.viewer.add(repr) - this.props.viewer.requestDraw() - } + if (state.visible !== undefined) props.visible = state.visible + if (state.quality !== undefined) props.quality = state.quality + + await repr.createOrUpdate(props).run() + this.props.viewer.add(repr) + this.props.viewer.requestDraw() const newState = { ...this.state, visible: repr.props.visible, + quality: repr.props.quality } this.setState(newState) } render() { - const { label, visible } = this.state + const { label, visible, quality } = this.state return <div> <div> @@ -64,6 +71,17 @@ export class StructureRepresentationComponent extends React.Component<StructureR {visible ? 'Hide' : 'Show'} </button> </div> + <div> + <span>Quality</span> + <select value={quality} onChange={(e) => this.update({ quality: e.target.value as VisualQuality }) }> + <option value='auto'>auto</option> + <option value='lowest'>lowest</option> + <option value='low'>low</option> + <option value='medium'>medium</option> + <option value='high'>high</option> + <option value='highest'>highest</option> + </select> + </div> </div> </div>; } diff --git a/src/apps/canvas/component/structure-view.tsx b/src/apps/canvas/component/structure-view.tsx index 7c8fb2119..7f3fdaab6 100644 --- a/src/apps/canvas/component/structure-view.tsx +++ b/src/apps/canvas/component/structure-view.tsx @@ -33,7 +33,8 @@ export interface StructureViewComponentState { symmetryFeatureId: number symmetryFeatureIds: { id: number, label: string }[] - structureRepresentations: StructureRepresentation<any>[] + active: { [k: string]: boolean } + structureRepresentations: { [k: string]: StructureRepresentation<any> } } export class StructureViewComponent extends React.Component<StructureViewComponentProps, StructureViewComponentState> { @@ -46,6 +47,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone symmetryFeatureId: this.props.structureView.symmetryFeatureId, symmetryFeatureIds: this.props.structureView.getSymmetryFeatureIds(), + active: this.props.structureView.active, structureRepresentations: this.props.structureView.structureRepresentations } @@ -62,19 +64,25 @@ export class StructureViewComponent extends React.Component<StructureViewCompone symmetryFeatureId: sv.symmetryFeatureId, symmetryFeatureIds: sv.getSymmetryFeatureIds(), + active: sv.active, structureRepresentations: sv.structureRepresentations }) } componentDidMount() { - this.props.structureView.structureRepresentationsUpdated.subscribe(() => this.setState({ - structureRepresentations: this.props.structureView.structureRepresentations + const sv = this.props.structureView + + this.props.structureView.updated.subscribe(() => this.setState({ + symmetryFeatureIds: sv.getSymmetryFeatureIds(), + structureRepresentations: sv.structureRepresentations })) } async update(state: Partial<StructureViewComponentState>) { const sv = this.props.structureView + console.log(state) + if (state.modelId !== undefined) await sv.setModel(state.modelId) if (state.assemblyId !== undefined) await sv.setAssembly(state.assemblyId) if (state.symmetryFeatureId !== undefined) await sv.setSymmetryFeature(state.symmetryFeatureId) @@ -89,13 +97,14 @@ export class StructureViewComponent extends React.Component<StructureViewCompone symmetryFeatureId: sv.symmetryFeatureId, symmetryFeatureIds: sv.getSymmetryFeatureIds(), + active: sv.active, structureRepresentations: sv.structureRepresentations } this.setState(newState) } render() { - const { label, modelIds, assemblyIds, symmetryFeatureIds, structureRepresentations } = this.state + const { label, modelIds, assemblyIds, symmetryFeatureIds, active, structureRepresentations } = this.state const modelIdOptions = modelIds.map(m => { return <option key={m.id} value={m.id}>{m.label}</option> @@ -122,6 +131,16 @@ export class StructureViewComponent extends React.Component<StructureViewCompone > {modelIdOptions} </select> + <input type='range' + value={this.state.modelId} + min={Math.min(...modelIds.map(m => m.id))} + max={Math.max(...modelIds.map(m => m.id))} + step='1' + onInput={(e) => { + this.update({ modelId: parseInt(e.currentTarget.value) }) + }} + > + </input> </div> <div> <span>Assembly</span> @@ -145,15 +164,39 @@ export class StructureViewComponent extends React.Component<StructureViewCompone {symmetryFeatureIdOptions} </select> </div> + <div> + <h4>Active</h4> + { Object.keys(active).map((k, i) => { + return <div key={i}> + <input + type='checkbox' + checked={active[k]} + onChange={(e) => { + const sv = this.props.structureView + if (k === 'symmetryAxes') { + sv.setSymmetryAxes(e.target.checked) + } else if (Object.keys(sv.structureRepresentations).includes(k)) { + sv.setStructureRepresentation(k, e.target.checked) + } + }} + /> {k} + </div> + } ) } + </div> <div> <h3>Structure Representations</h3> - { structureRepresentations ? structureRepresentations.map((r, i) => - <div key={i}> - <StructureRepresentationComponent - representation={r} - viewer={this.props.structureView.viewer} - /> - </div>) : '' } + { Object.keys(structureRepresentations).map((k, i) => { + if (active[k]) { + return <div key={i}> + <StructureRepresentationComponent + representation={structureRepresentations[k]} + viewer={this.props.structureView.viewer} + /> + </div> + } else { + return '' + } + } ) } </div> </div> </div>; diff --git a/src/apps/canvas/index.html b/src/apps/canvas/index.html index aa8b418e9..8bd125a03 100644 --- a/src/apps/canvas/index.html +++ b/src/apps/canvas/index.html @@ -5,16 +5,16 @@ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <title>Mol* Canvas</title> <style> - * { - margin: 0; - padding: 0; - } - html, body { - width: 100%; - height: 100%; - overflow: hidden; - } - </style> + * { + margin: 0; + padding: 0; + } + html, body { + width: 100%; + height: 100%; + overflow: hidden; + } + </style> </head> <body> <div id="app" style="width: 100%; height: 100%"></div> diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index 1b25ee11a..cfffbbb50 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -31,10 +31,14 @@ export interface StructureView { readonly structure: Structure | undefined readonly assemblySymmetry: AssemblySymmetry | undefined - readonly structureRepresentations: StructureRepresentation<any>[] - readonly structureRepresentationsUpdated: BehaviorSubject<null> + readonly active: { [k: string]: boolean } + readonly structureRepresentations: { [k: string]: StructureRepresentation<any> } + readonly updated: BehaviorSubject<null> readonly symmetryAxes: ShapeRepresentation<ShapeProps> + setSymmetryAxes(value: boolean): void + setStructureRepresentation(name: string, value: boolean): void + readonly modelId: number readonly assemblyId: string readonly symmetryFeatureId: number @@ -54,23 +58,29 @@ interface StructureViewProps { symmetryFeatureId?: number } + + export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model>, props: StructureViewProps = {}): Promise<StructureView> { - const cartoon = CartoonRepresentation() - const point = PointRepresentation() - const ballAndStick = BallAndStickRepresentation() - const carbohydrate = CarbohydrateRepresentation() - - const structureRepresentations: StructureRepresentation<any>[] = [ - // cartoon, - point, - // ballAndStick, - // carbohydrate - ] + const active: { [k: string]: boolean } = { + cartoon: true, + point: false, + ballAndStick: false, + carbohydrate: false, + symmetryAxes: false, + polymerSphere: false, + } + + const structureRepresentations: { [k: string]: StructureRepresentation<any> } = { + cartoon: CartoonRepresentation(), + point: PointRepresentation(), + ballAndStick: BallAndStickRepresentation(), + carbohydrate: CarbohydrateRepresentation(), + } const symmetryAxes = ShapeRepresentation() const polymerSphere = ShapeRepresentation() - const structureRepresentationsUpdated: BehaviorSubject<null> = new BehaviorSubject<null>(null) + const updated: BehaviorSubject<null> = new BehaviorSubject<null>(null) let label: string let model: Model | undefined @@ -81,12 +91,30 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> let assemblyId: string let symmetryFeatureId: number + async function setSymmetryAxes(value: boolean) { + if (!value) { + assemblySymmetry = undefined + } else { + await AssemblySymmetry.attachFromCifOrAPI(models[modelId]) + assemblySymmetry = AssemblySymmetry.get(models[modelId]) + } + active.symmetryAxes = value + await setSymmetryFeature() + } + + async function setStructureRepresentation(k: string, value: boolean) { + active[k] = value + await createStructureRepr() + } + async function setModel(newModelId: number, newAssemblyId?: string, newSymmetryFeatureId?: number) { console.log('setModel', newModelId) modelId = newModelId model = models[modelId] - await AssemblySymmetry.attachFromCifOrAPI(model) - assemblySymmetry = AssemblySymmetry.get(model) + if (active.symmetryAxes) { + await AssemblySymmetry.attachFromCifOrAPI(model) + assemblySymmetry = AssemblySymmetry.get(model) + } await setAssembly(newAssemblyId, newSymmetryFeatureId) } @@ -169,8 +197,13 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> async function createStructureRepr() { if (structure) { console.log('createStructureRepr') - for (let i = 0, il = structureRepresentations.length; i < il; ++i) { - await structureRepresentations[i].createOrUpdate({}, structure).run() + for (const k in structureRepresentations) { + if (active[k]) { + await structureRepresentations[k].createOrUpdate({}, structure).run() + viewer.add(structureRepresentations[k]) + } else { + viewer.remove(structureRepresentations[k]) + } } viewer.center(structure.boundary.sphere.center) @@ -197,13 +230,14 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> // useFog: false // TODO fog not working properly // }, shape).run() } else { - structureRepresentations.forEach(repr => repr.destroy) + for (const k in structureRepresentations) structureRepresentations[k].destroy() polymerSphere.destroy() } - structureRepresentations.forEach(repr => viewer.add(repr)) viewer.add(polymerSphere) - structureRepresentationsUpdated.next(null) + + updated.next(null) + viewer.requestDraw() } async function createSymmetryRepr() { @@ -224,16 +258,17 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> // useFog: false // TODO fog not working properly // }).run() await symmetryAxes.createOrUpdate({}, axesShape).run() + viewer.add(symmetryAxes) } else { - symmetryAxes.destroy() + viewer.remove(symmetryAxes) } } else { - symmetryAxes.destroy() + viewer.remove(symmetryAxes) } } else { - symmetryAxes.destroy() + viewer.remove(symmetryAxes) } - viewer.add(symmetryAxes) + updated.next(null) viewer.requestDraw() } @@ -247,10 +282,14 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> get structure() { return structure }, get assemblySymmetry() { return assemblySymmetry }, + active, structureRepresentations, - structureRepresentationsUpdated, + updated, symmetryAxes, + setSymmetryAxes, + setStructureRepresentation, + get modelId() { return modelId }, get assemblyId() { return assemblyId }, get symmetryFeatureId() { return symmetryFeatureId }, @@ -263,10 +302,10 @@ export async function StructureView(viewer: Viewer, models: ReadonlyArray<Model> getSymmetryFeatureIds, destroy: () => { - structureRepresentations.forEach(repr => { - viewer.remove(repr) - repr.destroy() - }) + for (const k in structureRepresentations) { + viewer.remove(structureRepresentations[k]) + structureRepresentations[k].destroy() + } viewer.remove(polymerSphere) viewer.remove(symmetryAxes) viewer.requestDraw() -- GitLab