Quellcode durchsuchen

ui, spacefill visibility and structure center

Alexander Rose vor 6 Jahren
Ursprung
Commit
0e3f6d8cce

+ 1 - 1
src/mol-app/ui/controls/common.tsx

@@ -88,7 +88,7 @@ export function isEnter(e: React.KeyboardEvent<HTMLInputElement>) {
 export function TextBoxGroup(props: {
     value: string,
     onChange: (v: string) => void,
-    placeholder?:string,
+    placeholder?: string,
     label: string,
     onEnter?: (e: React.KeyboardEvent<HTMLInputElement>) => void
     title?: string

+ 2 - 2
src/mol-app/ui/entity/tree.tsx

@@ -13,7 +13,7 @@ import { View } from '../view';
 import { EntityTreeController } from '../../controller/entity/tree';
 import { Controller } from '../../controller/controller';
 import { AnyEntity, RootEntity } from 'mol-view/state/entity';
-import { AnyTransform, SpacefillUpdate, UrlToData, DataToCif, FileToData, CifToMmcif, MmcifToModel, ModelToStructure, StructureToSpacefill, MmcifFileToSpacefill } from 'mol-view/state/transform';
+import { AnyTransform, SpacefillUpdate, UrlToData, DataToCif, FileToData, CifToMmcif, MmcifToModel, ModelToStructure, StructureToSpacefill, MmcifFileToSpacefill, StructureCenter } from 'mol-view/state/transform';
 
 function getTransforms(entity: AnyEntity): AnyTransform[] {
     const transforms: AnyTransform[] = []
@@ -40,7 +40,7 @@ function getTransforms(entity: AnyEntity): AnyTransform[] {
             transforms.push(ModelToStructure)
             break;
         case 'structure':
-            transforms.push(StructureToSpacefill)
+            transforms.push(StructureToSpacefill, StructureCenter)
             break;
         case 'spacefill':
             transforms.push(SpacefillUpdate)

+ 3 - 0
src/mol-app/ui/transform/list.tsx

@@ -17,6 +17,7 @@ import { Spacefill } from './spacefill';
 import { AnyEntity } from 'mol-view/state/entity';
 import { FileLoader } from './file-loader';
 import { ModelToStructure } from './model';
+import { StructureCenter } from './structure';
 
 function getTransformComponent(controller: TransformListController, entity: AnyEntity, transform: AnyTransform) {
     switch (transform.kind) {
@@ -24,6 +25,8 @@ function getTransformComponent(controller: TransformListController, entity: AnyE
             return <FileLoader controller={controller} ctx={controller.context.stage.ctx}></FileLoader>
         case 'model-to-structure':
             return <ModelToStructure controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></ModelToStructure>
+        case 'structure-center':
+            return <StructureCenter controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></StructureCenter>
         case 'spacefill-update':
             return <Spacefill controller={controller} entity={entity} transform={transform} ctx={controller.context.stage.ctx}></Spacefill>
     }

+ 29 - 1
src/mol-app/ui/transform/spacefill.tsx

@@ -11,11 +11,14 @@ import * as React from 'react'
 
 import { View } from '../view';
 import { Controller } from '../../controller/controller';
+import { Toggle } from '../controls/common';
 import { SpacefillEntity } from 'mol-view/state/entity';
 import { SpacefillUpdate } from 'mol-view/state/transform'
 import { StateContext } from 'mol-view/state/context';
 import { ColorTheme } from 'mol-geo/theme';
 import { Color, ColorNames } from 'mol-util/color';
+import { Query, Queries as Q } from 'mol-model/structure';
+import { Slider } from '../controls/slider';
 
 export const ColorThemeInfo = {
     'atom-index': {},
@@ -31,6 +34,8 @@ interface SpacefillState {
     detail: number
     colorTheme: ColorTheme
     colorValue: Color
+    visible: boolean
+    alpha: number
 }
 
 export class Spacefill extends View<Controller<any>, SpacefillState, { transform: SpacefillUpdate, entity: SpacefillEntity, ctx: StateContext }> {
@@ -38,7 +43,9 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform
         doubleSided: true,
         detail: 2,
         colorTheme: { name: 'element-symbol' } as ColorTheme,
-        colorValue: 0x000000
+        colorValue: 0x000000,
+        visible: true,
+        alpha: 1
     }
 
     update(state?: Partial<SpacefillState>) {
@@ -135,6 +142,27 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform
                                 </select>
                             </div>
                         </div>
+                        <div className='molstar-control-row molstar-options-group'>
+                            <div>
+                                <Toggle
+                                    value={this.state.visible}
+                                    label='Visibility'
+                                    onChange={value => this.update({ visible: value })}
+                                />
+                            </div>
+                        </div>
+                        {/* <div className='molstar-control-row molstar-options-group'>
+                            <div>
+                                <Slider
+                                    value={this.state.alpha}
+                                    label='Opacity'
+                                    min={0}
+                                    max={1}
+                                    step={0.01}
+                                    onChange={value => this.update({ alpha: value })}
+                                />
+                            </div>
+                        </div> */}
                     </div>
                 </div>
             </div>

+ 61 - 0
src/mol-app/ui/transform/structure.tsx

@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * Adapted from LiteMol
+ * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as React from 'react'
+
+import { View } from '../view';
+import { Controller } from '../../controller/controller';
+import { Button } from '../controls/common';
+import { StructureEntity } from 'mol-view/state/entity';
+import { StructureCenter as StructureCenterTransform } from 'mol-view/state/transform'
+import { StateContext } from 'mol-view/state/context';
+
+export const ColorThemeInfo = {
+    'atom-index': {},
+    'chain-id': {},
+    'element-symbol': {},
+    'instance-index': {},
+    'uniform': {}
+}
+export type ColorThemeInfo = keyof typeof ColorThemeInfo
+
+export class StructureCenter extends View<Controller<any>, {}, { transform: StructureCenterTransform, entity: StructureEntity, ctx: StateContext }> {
+    center() {
+        const { ctx, entity, transform } = this.props
+        transform.apply(ctx, entity)
+    }
+
+    render() {
+        const { transform } = this.props
+
+        return <div className='molstar-transformer-wrapper'>
+            <div className='molstar-panel molstar-control molstar-transformer molstar-panel-expanded'>
+                <div className='molstar-panel-header'>
+                    <button
+                        className='molstar-btn molstar-btn-link molstar-panel-expander'
+                        onClick={() => {}}
+                    >
+                        <span>[{transform.kind}] {transform.inputKind} -> {transform.outputKind}</span>
+                    </button>
+                </div>
+                <div className='molstar-panel-body'>
+                    <div>
+                        <div className='molstar-control-row molstar-options-group'>
+                            <div>
+                                <Button onClick={value => this.center()}>
+                                    Center
+                                </Button>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>;
+    }
+}

+ 18 - 4
src/mol-geo/representation/structure/index.ts

@@ -9,8 +9,7 @@ import { Structure, StructureSymmetry, Unit } from 'mol-model/structure';
 import { Task } from 'mol-task'
 import { RenderObject } from 'mol-gl/scene';
 import { Representation, RepresentationProps } from '..';
-// import { Mat4, EPSILON } from 'mol-math/linear-algebra';
-
+import { ColorTheme } from '../../theme';
 
 export interface UnitsRepresentation<P> {
     renderObjects: ReadonlyArray<RenderObject>
@@ -29,14 +28,29 @@ interface GroupRepresentation<T> {
     group: Unit.SymmetryGroup
 }
 
-export function StructureRepresentation<P>(reprCtor: () => UnitsRepresentation<P>): StructureRepresentation<P> {
+export const DefaultStructureProps = {
+    colorTheme: { name: 'instance-index' } as ColorTheme,
+    alpha: 1,
+    visible: true,
+    doubleSided: false
+}
+export type StructureProps = Partial<typeof DefaultStructureProps>
+
+export function StructureRepresentation<P extends StructureProps>(reprCtor: () => UnitsRepresentation<P>): StructureRepresentation<P> {
     const renderObjects: RenderObject[] = []
     const groupReprs: GroupRepresentation<P>[] = []
+    // let currentProps: typeof DefaultStructureProps
 
     return {
         renderObjects,
         create(structure: Structure, props: P = {} as P) {
+            // currentProps = Object.assign({}, DefaultStructureProps, props)
+
             return Task.create('StructureRepresentation.create', async ctx => {
+                // const { query } = currentProps
+                // const qs = await query(structure).runAsChild(ctx)
+                // const subStructure = Selection.unionStructure(qs)
+
                 const groups = StructureSymmetry.getTransformGroups(structure);
                 for (let i = 0; i < groups.length; i++) {
                     const group = groups[i];
@@ -49,8 +63,8 @@ export function StructureRepresentation<P>(reprCtor: () => UnitsRepresentation<P
         },
         update(props: P) {
             return Task.create('StructureRepresentation.update', async ctx => {
-                // TODO check model.id, conformation.id, unit.id, elementGroup(.hashCode/.areEqual)
                 renderObjects.length = 0 // clear
+
                 for (let i = 0, il = groupReprs.length; i < il; ++i) {
                     const groupRepr = groupReprs[i]
                     const { repr, group } = groupRepr

+ 16 - 7
src/mol-geo/representation/structure/spacefill.ts

@@ -11,20 +11,16 @@ import { RenderObject, createMeshRenderObject, MeshRenderObject } from 'mol-gl/s
 // import { createColorTexture } from 'mol-gl/util';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { Unit, Element, Queries } from 'mol-model/structure';
-import { UnitsRepresentation } from './index';
+import { UnitsRepresentation, DefaultStructureProps } from './index';
 import { Task } from 'mol-task'
 import { MeshBuilder } from '../../shape/mesh-builder';
 import { createTransforms, createColors } from './utils';
-import { ColorTheme } from '../../theme';
 import VertexMap from '../../shape/vertex-map';
 import { icosahedronVertexCount } from '../../primitive/icosahedron';
 
 export const DefaultSpacefillProps = {
+    ...DefaultStructureProps,
     detail: 0,
-    colorTheme: { name: 'instance-index' } as ColorTheme,
-    alpha: 1,
-    visible: true,
-    doubleSided: false
 }
 export type SpacefillProps = Partial<typeof DefaultSpacefillProps>
 
@@ -74,10 +70,13 @@ function createSpacefillMesh(unit: Unit, detail: number) {
 export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
     const renderObjects: RenderObject[] = []
     let spheres: MeshRenderObject
+    let currentProps: typeof DefaultSpacefillProps
 
     return {
         renderObjects,
         create(group: Unit.SymmetryGroup, props: SpacefillProps = {}) {
+            currentProps = Object.assign({}, DefaultSpacefillProps, props)
+
             return Task.create('Spacefill.create', async ctx => {
                 renderObjects.length = 0 // clear
 
@@ -116,10 +115,20 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
             })
         },
         update(props: SpacefillProps) {
+            const newProps = Object.assign({}, currentProps, props)
+
             return Task.create('Spacefill.update', async ctx => {
                 if (!spheres) return false
+                if (props.detail !== currentProps.detail) return false
+                if (props.colorTheme !== currentProps.colorTheme) return false
+                if (props.detail !== currentProps.detail) return false
+
+                spheres.props.alpha = newProps.alpha
+                spheres.props.visible = newProps.visible
+                spheres.props.doubleSided = newProps.doubleSided
 
-                return false
+                currentProps = newProps
+                return true
             })
         }
     }

+ 4 - 1
src/mol-view/controls/trackball.ts

@@ -15,6 +15,7 @@ import InputObserver, { DragInput, WheelInput, ButtonsFlag, PinchInput } from 'm
 
 export const DefaultTrackballControlsProps = {
     noScroll: true,
+    target: [0, 0, 0] as Vec3,
 
     rotateSpeed: 3.0,
     zoomSpeed: 4.0,
@@ -36,6 +37,7 @@ interface Object {
 
 interface TrackballControls {
     viewport: Viewport
+    target: Vec3
 
     dynamicDampingFactor: number
     rotateSpeed: number
@@ -52,6 +54,7 @@ namespace TrackballControls {
         const p = { ...DefaultTrackballControlsProps, ...props }
 
         const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 }
+        const target: Vec3 = Vec3.clone(p.target)
 
         let { rotateSpeed, zoomSpeed, panSpeed } = p
         let { staticMoving, dynamicDampingFactor } = p
@@ -64,7 +67,6 @@ namespace TrackballControls {
         const pinchSub = input.pinch.subscribe(onPinch)
 
         // For internal use
-        const target = Vec3.zero()
         const lastPosition = Vec3.zero()
 
         const _eye = Vec3.zero()
@@ -297,6 +299,7 @@ namespace TrackballControls {
 
         return {
             viewport,
+            target,
 
             get dynamicDampingFactor() { return dynamicDampingFactor },
             set dynamicDampingFactor(value: number ) { dynamicDampingFactor = value },

+ 2 - 0
src/mol-view/state/entity.ts

@@ -31,7 +31,9 @@ export namespace StateEntity {
 }
 
 export type AnyEntity = StateEntity<any, any>
+export type NullEntity = StateEntity<null, 'null'>
 
+export const NullEntity: NullEntity = { id: -1, kind: 'null', value: null }
 export const RootEntity: StateEntity<null, 'root'> = { id: 0, kind: 'root', value: null }
 
 export interface UrlProps {

+ 23 - 7
src/mol-view/state/transform.ts

@@ -5,7 +5,7 @@
  */
 
 import CIF from 'mol-io/reader/cif'
-import { FileEntity, DataEntity, UrlEntity, CifEntity, MmcifEntity, ModelEntity, StructureEntity, SpacefillEntity, AnyEntity } from './entity';
+import { FileEntity, DataEntity, UrlEntity, CifEntity, MmcifEntity, ModelEntity, StructureEntity, SpacefillEntity, AnyEntity, NullEntity } from './entity';
 import { Model, Structure } from 'mol-model/structure';
 
 import { StateContext } from './context';
@@ -80,9 +80,17 @@ export const ModelToStructure: ModelToStructure = StateTransform.create('model',
         } else {
             structure = Structure.ofModel(model)
         }
+        console.log('center', structure.boundary.sphere.center)
         return StructureEntity.ofStructure(ctx, structure)
     })
 
+export type StructureCenter = StateTransform<StructureEntity, NullEntity, {}>
+    export const StructureCenter: StructureCenter = StateTransform.create('structure', 'null', 'structure-center',
+        async function (ctx: StateContext, structureEntity: StructureEntity) {
+            ctx.viewer.center(structureEntity.value.boundary.sphere.center)
+            return NullEntity
+        })
+
 export type StructureToSpacefill = StateTransform<StructureEntity, SpacefillEntity, SpacefillProps>
 export const StructureToSpacefill: StructureToSpacefill = StateTransform.create('structure', 'spacefill', 'structure-to-spacefill',
     async function (ctx: StateContext, structureEntity: StructureEntity, props: SpacefillProps = {}) {
@@ -90,21 +98,29 @@ export const StructureToSpacefill: StructureToSpacefill = StateTransform.create(
         await spacefillRepr.create(structureEntity.value, props).run(ctx.log)
         ctx.viewer.add(spacefillRepr)
         ctx.viewer.requestDraw()
-        console.log(ctx.viewer.stats)
+        console.log(ctx.viewer.stats, props)
+        // ctx.viewer.input.drag.subscribe(async () => {
+        //     console.log('drag')
+        //     console.time('spacefill update')
+        //     await spacefillRepr.update(props).run(ctx.log)
+        //     console.timeEnd('spacefill update')
+        //     ctx.viewer.add(spacefillRepr)
+        //     ctx.viewer.update()
+        //     ctx.viewer.requestDraw()
+        // })
         return SpacefillEntity.ofRepr(ctx, spacefillRepr)
     })
 
-export type SpacefillUpdate = StateTransform<SpacefillEntity, SpacefillEntity, SpacefillProps>
-export const SpacefillUpdate: SpacefillUpdate = StateTransform.create('spacefill', 'spacefill', 'spacefill-update',
+export type SpacefillUpdate = StateTransform<SpacefillEntity, NullEntity, SpacefillProps>
+export const SpacefillUpdate: SpacefillUpdate = StateTransform.create('spacefill', 'null', 'spacefill-update',
     async function (ctx: StateContext, spacefillEntity: SpacefillEntity, props: SpacefillProps = {}) {
-        console.log('fopbar')
         const spacefillRepr = spacefillEntity.value
         await spacefillRepr.update(props).run(ctx.log)
         ctx.viewer.add(spacefillRepr)
         ctx.viewer.update()
         ctx.viewer.requestDraw()
-        console.log(ctx.viewer.stats)
-        return spacefillEntity
+        console.log(ctx.viewer.stats, props)
+        return NullEntity
     })
 
 // composed

+ 10 - 0
src/mol-view/viewer.ts

@@ -20,6 +20,8 @@ import { createContext } from 'mol-gl/webgl/context';
 import { Representation } from 'mol-geo/representation';
 
 interface Viewer {
+    center: (p: Vec3) => void
+
     hide: (repr: Representation<any>) => void
     show: (repr: Representation<any>) => void
 
@@ -37,6 +39,7 @@ interface Viewer {
     resetCamera: () => void
     downloadScreenshot: () => void
 
+    input: InputObserver
     stats: RendererStats
     dispose: () => void
 }
@@ -110,6 +113,10 @@ namespace Viewer {
         handleResize()
 
         return {
+            center: (p: Vec3) => {
+                Vec3.set(controls.target, p[0], p[1], p[2])
+            },
+
             hide: (repr: Representation<any>) => {
                 const renderObjectSet = reprMap.get(repr)
                 if (renderObjectSet) renderObjectSet.forEach(o => o.props.visible = false)
@@ -159,6 +166,9 @@ namespace Viewer {
             },
             reprCount,
 
+            get input() {
+                return input
+            },
             get stats() {
                 return renderer.stats
             },