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