From ca13c576bf0ce5440bd62b333dd7f8ff5ab2798c Mon Sep 17 00:00:00 2001
From: Alexander Rose <alex.rose@rcsb.org>
Date: Wed, 5 Sep 2018 18:46:37 -0700
Subject: [PATCH] wip, canvas example

---
 src/apps/canvas/app.ts                        | 19 ++++--
 src/apps/canvas/component/app.tsx             | 37 +++++++++--
 .../component/structure-representation.tsx    | 25 +++++---
 src/apps/canvas/component/structure-view.tsx  | 64 ++++++++-----------
 src/apps/canvas/util.ts                       | 13 +++-
 .../representation/structure/units-visual.ts  |  1 +
 src/mol-geo/representation/util.ts            | 12 +++-
 7 files changed, 113 insertions(+), 58 deletions(-)

diff --git a/src/apps/canvas/app.ts b/src/apps/canvas/app.ts
index 340aa4869..9aeeccf00 100644
--- a/src/apps/canvas/app.ts
+++ b/src/apps/canvas/app.ts
@@ -5,9 +5,10 @@
  */
 
 import Viewer from 'mol-view/viewer';
-import { getCifFromUrl, getModelsFromMmcif } from './util';
+import { getCifFromUrl, getModelsFromMmcif, getCifFromFile } from './util';
 import { StructureView } from './structure-view';
 import { BehaviorSubject } from 'rxjs';
+import { CifBlock } from 'mol-io/reader/cif';
 
 export class App {
     viewer: Viewer
@@ -31,11 +32,21 @@ export class App {
         }
     }
 
-    async loadPdbId(id: string, assemblyId?: string) {
-        if (this.structureView) this.structureView.destroy()
-        const cif = await getCifFromUrl(`https://files.rcsb.org/download/${id}.cif`)
+    async loadCif(cif: CifBlock, assemblyId?: string) {
         const models = await getModelsFromMmcif(cif)
         this.structureView = await StructureView(this.viewer, models, { assemblyId })
         this.pdbIdLoaded.next(this.structureView)
     }
+
+    async loadPdbId(id: string, assemblyId?: string) {
+        if (this.structureView) this.structureView.destroy()
+        const cif = await getCifFromUrl(`https://files.rcsb.org/download/${id}.cif`)
+        this.loadCif(cif, assemblyId)
+    }
+
+    async loadCifFile(file: File) {
+        if (this.structureView) this.structureView.destroy()
+        const cif = await getCifFromFile(file)
+        this.loadCif(cif)
+    }
 }
\ No newline at end of file
diff --git a/src/apps/canvas/component/app.tsx b/src/apps/canvas/component/app.tsx
index 6663b10bb..2186e90c6 100644
--- a/src/apps/canvas/component/app.tsx
+++ b/src/apps/canvas/component/app.tsx
@@ -35,9 +35,11 @@ export class AppComponent extends React.Component<AppProps, AppState> {
     }
 
     componentDidMount() {
-        this.props.app.pdbIdLoaded.subscribe(() => this.setState({
-            structureView: this.props.app.structureView
-        }))
+        this.props.app.pdbIdLoaded.subscribe((structureView) => {
+            this.setState({
+                structureView: this.props.app.structureView
+            })
+        })
     }
 
     render() {
@@ -49,7 +51,34 @@ export class AppComponent extends React.Component<AppProps, AppState> {
             </div>
 
             <div style={{float: 'right', width: '25%', height: '100%'}}>
-                {structureView ? <StructureViewComponent structureView={structureView} /> : ''}
+                <div>
+                    <span>Load PDB ID</span>
+                    <input
+                        type='text'
+                        onKeyDown={e => {
+                            if (e.keyCode === 13) {
+                                const value = e.currentTarget.value.trim()
+                                if (value) {
+                                    this.props.app.loadPdbId(value)
+                                }
+                            }
+                        }}
+                    />
+                </div>
+                <div>
+                    <span>Load CIF file</span>
+                    <input
+                        accept='*.cif'
+                        type='file'
+                        onChange={e => {
+                            if (e.target.files) this.props.app.loadCifFile(e.target.files[0])
+                        }}
+                    />
+                </div>
+                <hr/>
+                <div>
+                    {structureView ? <StructureViewComponent structureView={structureView} /> : ''}
+                </div>
             </div>
         </div>;
     }
diff --git a/src/apps/canvas/component/structure-representation.tsx b/src/apps/canvas/component/structure-representation.tsx
index 3a08a80ca..0a0b12fe8 100644
--- a/src/apps/canvas/component/structure-representation.tsx
+++ b/src/apps/canvas/component/structure-representation.tsx
@@ -7,7 +7,8 @@
 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';
+import { VisualQuality, VisualQualityNames } from 'mol-geo/representation/util';
+import { ColorThemeProps, ColorThemeName, ColorThemeNames } from 'mol-view/theme/color';
 
 export interface StructureRepresentationComponentProps {
     viewer: Viewer
@@ -18,6 +19,7 @@ export interface StructureRepresentationComponentState {
     label: string
     visible: boolean
     quality: VisualQuality
+    colorTheme: ColorThemeProps
 }
 
 export class StructureRepresentationComponent extends React.Component<StructureRepresentationComponentProps, StructureRepresentationComponentState> {
@@ -25,6 +27,7 @@ export class StructureRepresentationComponent extends React.Component<StructureR
         label: this.props.representation.label,
         visible: this.props.representation.props.visible,
         quality: this.props.representation.props.quality,
+        colorTheme: this.props.representation.props.colorTheme,
     }
 
     componentWillMount() {
@@ -35,6 +38,7 @@ export class StructureRepresentationComponent extends React.Component<StructureR
             label: repr.label,
             visible: repr.props.visible,
             quality: repr.props.quality,
+            colorTheme: repr.props.colorTheme,
         })
     }
 
@@ -44,6 +48,7 @@ export class StructureRepresentationComponent extends React.Component<StructureR
 
         if (state.visible !== undefined) props.visible = state.visible
         if (state.quality !== undefined) props.quality = state.quality
+        if (state.colorTheme !== undefined) props.colorTheme = state.colorTheme
 
         await repr.createOrUpdate(props).run()
         this.props.viewer.add(repr)
@@ -52,13 +57,14 @@ export class StructureRepresentationComponent extends React.Component<StructureR
         const newState = {
             ...this.state,
             visible: repr.props.visible,
-            quality: repr.props.quality
+            quality: repr.props.quality,
+            colorTheme: repr.props.colorTheme,
         }
         this.setState(newState)
     }
 
     render() {
-        const { label, visible, quality } = this.state
+        const { label, visible, quality, colorTheme } = this.state
 
         return <div>
             <div>
@@ -74,12 +80,13 @@ export class StructureRepresentationComponent extends React.Component<StructureR
                 <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>
+                        {VisualQualityNames.map(name => <option key={name} value={name}>{name}</option>)}
+                    </select>
+                </div>
+                <div>
+                    <span>Color Theme</span>
+                    <select value={colorTheme.name} onChange={(e) => this.update({ colorTheme: { name: e.target.value as ColorThemeName } }) }>
+                        {ColorThemeNames.map(name => <option key={name} value={name}>{name}</option>)}
                     </select>
                 </div>
             </div>
diff --git a/src/apps/canvas/component/structure-view.tsx b/src/apps/canvas/component/structure-view.tsx
index 7f3fdaab6..c00eb8e83 100644
--- a/src/apps/canvas/component/structure-view.tsx
+++ b/src/apps/canvas/component/structure-view.tsx
@@ -25,6 +25,8 @@ export interface StructureViewComponentProps {
 }
 
 export interface StructureViewComponentState {
+    structureView: StructureView
+
     label: string
     modelId: number
     modelIds: { id: number, label: string }[]
@@ -38,24 +40,12 @@ export interface StructureViewComponentState {
 }
 
 export class StructureViewComponent extends React.Component<StructureViewComponentProps, StructureViewComponentState> {
-    state = {
-        label: this.props.structureView.label,
-        modelId: this.props.structureView.modelId,
-        modelIds: this.props.structureView.getModelIds(),
-        assemblyId: this.props.structureView.assemblyId,
-        assemblyIds: this.props.structureView.getAssemblyIds(),
-        symmetryFeatureId: this.props.structureView.symmetryFeatureId,
-        symmetryFeatureIds: this.props.structureView.getSymmetryFeatureIds(),
-
-        active: this.props.structureView.active,
-        structureRepresentations: this.props.structureView.structureRepresentations
-    }
+    state = this.stateFromStructureView(this.props.structureView)
 
-    componentWillMount() {
-        const sv = this.props.structureView
+    private stateFromStructureView(sv: StructureView) {
+        return {
+            structureView: sv,
 
-        this.setState({
-            ...this.state,
             label: sv.label,
             modelId: sv.modelId,
             modelIds: sv.getModelIds(),
@@ -66,7 +56,11 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
 
             active: sv.active,
             structureRepresentations: sv.structureRepresentations
-        })
+        }
+    }
+
+    componentWillMount() {
+        this.setState(this.stateFromStructureView(this.props.structureView))
     }
 
     componentDidMount() {
@@ -78,33 +72,29 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
         }))
     }
 
-    async update(state: Partial<StructureViewComponentState>) {
-        const sv = this.props.structureView
+    componentWillReceiveProps(nextProps: StructureViewComponentProps) {
+        if (nextProps.structureView !== this.props.structureView) {
+            this.setState(this.stateFromStructureView(nextProps.structureView))
 
-        console.log(state)
+            nextProps.structureView.updated.subscribe(() => this.setState({
+                symmetryFeatureIds: nextProps.structureView.getSymmetryFeatureIds(),
+                structureRepresentations: nextProps.structureView.structureRepresentations
+            }))
+        }
+    }
+
+    async update(state: Partial<StructureViewComponentState>) {
+        const sv = this.state.structureView
 
         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)
 
-        const newState = {
-            ...this.state,
-            label: sv.label,
-            modelId: sv.modelId,
-            modelIds: sv.getModelIds(),
-            assemblyId: sv.assemblyId,
-            assemblyIds: sv.getAssemblyIds(),
-            symmetryFeatureId: sv.symmetryFeatureId,
-            symmetryFeatureIds: sv.getSymmetryFeatureIds(),
-
-            active: sv.active,
-            structureRepresentations: sv.structureRepresentations
-        }
-        this.setState(newState)
+        this.setState(this.stateFromStructureView(sv))
     }
 
     render() {
-        const { label, modelIds, assemblyIds, symmetryFeatureIds, active, structureRepresentations } = this.state
+        const { structureView, label, modelIds, assemblyIds, symmetryFeatureIds, active, structureRepresentations } = this.state
 
         const modelIdOptions = modelIds.map(m => {
             return <option key={m.id} value={m.id}>{m.label}</option>
@@ -172,7 +162,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
                                 type='checkbox'
                                 checked={active[k]}
                                 onChange={(e) => {
-                                    const sv = this.props.structureView
+                                    const sv = structureView
                                     if (k === 'symmetryAxes') {
                                         sv.setSymmetryAxes(e.target.checked)
                                     } else if (Object.keys(sv.structureRepresentations).includes(k)) {
@@ -190,7 +180,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
                             return <div key={i}>
                                 <StructureRepresentationComponent
                                     representation={structureRepresentations[k]}
-                                    viewer={this.props.structureView.viewer}
+                                    viewer={structureView.viewer}
                                 />
                             </div>
                         } else {
diff --git a/src/apps/canvas/util.ts b/src/apps/canvas/util.ts
index f63797e10..9a2c8f7fa 100644
--- a/src/apps/canvas/util.ts
+++ b/src/apps/canvas/util.ts
@@ -5,7 +5,7 @@
  */
 
 import CIF, { CifBlock } from 'mol-io/reader/cif'
-import { readUrlAs } from 'mol-util/read';
+import { readUrlAs, readFileAs } from 'mol-util/read';
 import { Model, Format, StructureSymmetry, Structure } from 'mol-model/structure';
 // import { parse as parseObj } from 'mol-io/reader/obj/parser'
 
@@ -17,14 +17,21 @@ import { Model, Format, StructureSymmetry, Structure } from 'mol-model/structure
 //     return parsed.result
 // }
 
-export async function getCifFromUrl(url: string) {
-    const data = await readUrlAs(url, false)
+export async function getCifFromData(data: string | Uint8Array) {
     const comp = CIF.parse(data)
     const parsed = await comp.run()
     if (parsed.isError) throw parsed
     return parsed.result.blocks[0]
 }
 
+export async function getCifFromUrl(url: string) {
+    return getCifFromData(await readUrlAs(url, false))
+}
+
+export async function getCifFromFile(file: File, binary = false) {
+    return getCifFromData(await readFileAs(file, binary))
+}
+
 export async function getModelsFromMmcif(cif: CifBlock) {
     return await Model.create(Format.mmCIF(cif)).run()
 }
diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts
index fa7467ba0..8184f2f1b 100644
--- a/src/mol-geo/representation/structure/units-visual.ts
+++ b/src/mol-geo/representation/structure/units-visual.ts
@@ -131,6 +131,7 @@ export function UnitsMeshVisual<P extends UnitsMeshProps>(builder: UnitsMeshVisu
             } else {
                 if (group && !areGroupsIdentical(group, currentGroup)) {
                     currentGroup = group
+                    currentProps.colorTheme.structure = currentStructure
                 }
                 await update(ctx, props)
             }
diff --git a/src/mol-geo/representation/util.ts b/src/mol-geo/representation/util.ts
index 50c795ce0..39b116a3a 100644
--- a/src/mol-geo/representation/util.ts
+++ b/src/mol-geo/representation/util.ts
@@ -84,7 +84,17 @@ export function updateRenderableState(state: RenderableState, props: Required<Ba
     state.depthMask = props.depthMask
 }
 
-export type VisualQuality = 'custom' | 'auto' | 'highest' | 'high' | 'medium' | 'low' | 'lowest'
+export const VisualQualityInfo = {
+    'custom': {},
+    'auto': {},
+    'highest': {},
+    'high': {},
+    'medium': {},
+    'low': {},
+    'lowest': {},
+}
+export type VisualQuality = keyof typeof VisualQualityInfo
+export const VisualQualityNames = Object.keys(VisualQualityInfo)
 
 export interface QualityProps {
     quality: VisualQuality
-- 
GitLab