Преглед изворни кода

added orthographic camera & combined camera

Alexander Rose пре 6 година
родитељ
комит
0a00768a7f

+ 1 - 1
src/apps/canvas/component/structure-view.tsx

@@ -174,7 +174,7 @@ export class StructureViewComponent extends React.Component<StructureViewCompone
                             return <div key={i}>
                                 <RepresentationComponent
                                     repr={structureRepresentations[k] as Representation<any>}
-                                    canvas3d={structureView.viewer}
+                                    canvas3d={structureView.canvas3d}
                                     app={structureView.app}
                                 />
                             </div>

+ 30 - 1
src/apps/canvas/component/viewport.tsx

@@ -11,6 +11,7 @@ import { EmptyLoci, Loci, areLociEqual } from 'mol-model/loci';
 import { labelFirst } from 'mol-theme/label';
 import { ButtonsType } from 'mol-util/input/input-observer';
 import { throttleTime } from 'rxjs/operators'
+import { CombinedCameraMode } from 'mol-canvas3d/camera/combined';
 
 interface ViewportProps {
     app: App
@@ -20,6 +21,7 @@ interface ViewportState {
     noWebGl: boolean
     pickingInfo: string
     taskInfo: string
+    cameraMode: CombinedCameraMode
 }
 
 export class Viewport extends React.Component<ViewportProps, ViewportState> {
@@ -29,7 +31,8 @@ export class Viewport extends React.Component<ViewportProps, ViewportState> {
     state: ViewportState = {
         noWebGl: false,
         pickingInfo: '',
-        taskInfo: ''
+        taskInfo: '',
+        cameraMode: 'perspective'
     };
 
     handleResize() {
@@ -42,6 +45,8 @@ export class Viewport extends React.Component<ViewportProps, ViewportState> {
         }
         this.handleResize()
 
+        this.setState({ cameraMode: this.props.app.canvas3d.camera.mode })
+
         const canvas3d = this.props.app.canvas3d
 
         canvas3d.input.resize.subscribe(() => this.handleResize())
@@ -117,6 +122,30 @@ export class Viewport extends React.Component<ViewportProps, ViewportState> {
             >
                 {this.state.pickingInfo}
             </div>
+            <div
+                style={{
+                    position: 'absolute',
+                    bottom: 10,
+                    right: 10,
+                    padding: 10,
+                    color: 'lightgrey',
+                    background: 'rgba(0, 0, 0, 0.2)'
+                }}
+            >
+                <span>Camera mode </span>
+                <select
+                    value={this.state.cameraMode}
+                    style={{width: '150'}}
+                    onChange={e => {
+                        const cameraMode = e.target.value as CombinedCameraMode
+                        this.props.app.canvas3d.camera.mode = cameraMode
+                        this.setState({ cameraMode })
+                    }}
+                >
+                    <option value='perspective'>Perspective</option>
+                    <option value='orthographic'>Orthographic</option>
+                </select>
+            </div>
             { this.state.taskInfo ?
                 <div
                     style={{

+ 2 - 2
src/apps/canvas/index.ts

@@ -25,8 +25,8 @@ if (pdbId) app.loadPdbIdOrMmcifUrl(pdbId, { assemblyId })
 
 // app.loadPdbIdOrMmcifUrl('http://localhost:8091/ngl/data/1crn.cif')
 
-// app.loadPdbIdOrMmcifUrl('3pqr')
-// app.loadCcp4Url('http://localhost:8091/ngl/data/3pqr-mode0.ccp4')
+app.loadPdbIdOrMmcifUrl('3pqr')
+app.loadCcp4Url('http://localhost:8091/ngl/data/3pqr-mode0.ccp4')
 
 // app.loadPdbIdOrMmcifUrl('1lee')
 // app.loadCcp4Url('http://localhost:8091/ngl/data/1lee.ccp4')

+ 19 - 19
src/apps/canvas/structure-view.ts

@@ -29,7 +29,7 @@ import { App } from './app';
 
 export interface StructureView {
     readonly app: App
-    readonly viewer: Canvas3D
+    readonly canvas3d: Canvas3D
 
     readonly label: string
     readonly models: ReadonlyArray<Model>
@@ -63,7 +63,7 @@ interface StructureViewProps {
     symmetryFeatureId?: number
 }
 
-export async function StructureView(app: App, viewer: Canvas3D, models: ReadonlyArray<Model>, props: StructureViewProps = {}): Promise<StructureView> {
+export async function StructureView(app: App, canvas3d: Canvas3D, models: ReadonlyArray<Model>, props: StructureViewProps = {}): Promise<StructureView> {
     const active: { [k: string]: boolean } = {
         cartoon: true,
         point: false,
@@ -208,17 +208,17 @@ export async function StructureView(app: App, viewer: Canvas3D, models: Readonly
             console.log('createStructureRepr')
             for (const k in structureRepresentations) {
                 if (active[k]) {
-                    const p = { webgl: viewer.webgl }
+                    const p = { webgl: canvas3d.webgl }
                     await app.runTask(structureRepresentations[k].createOrUpdate(p, structure).run(
                         progress => app.log(progress)
                     ), 'Create/update representation')
-                    viewer.add(structureRepresentations[k])
+                    canvas3d.add(structureRepresentations[k])
                 } else {
-                    viewer.remove(structureRepresentations[k])
+                    canvas3d.remove(structureRepresentations[k])
                 }
             }
 
-            viewer.center(structure.boundary.sphere.center)
+            canvas3d.center(structure.boundary.sphere.center)
 
             // const mb = MeshBuilder.create()
             // mb.setGroup(0)
@@ -246,11 +246,11 @@ export async function StructureView(app: App, viewer: Canvas3D, models: Readonly
             polymerSphere.destroy()
         }
 
-        viewer.add(polymerSphere)
+        canvas3d.add(polymerSphere)
 
         updated.next(null)
-        viewer.requestDraw(true)
-        console.log('stats', viewer.stats)
+        canvas3d.requestDraw(true)
+        console.log('stats', canvas3d.stats)
     }
 
     async function createSymmetryRepr() {
@@ -266,25 +266,25 @@ export async function StructureView(app: App, viewer: Canvas3D, models: Readonly
                     //     colorGranularity: colorTheme.granularity,
                     // }).run()
                     await symmetryAxes.createOrUpdate({}, axesShape).run()
-                    viewer.add(symmetryAxes)
+                    canvas3d.add(symmetryAxes)
                 } else {
-                    viewer.remove(symmetryAxes)
+                    canvas3d.remove(symmetryAxes)
                 }
             } else {
-                viewer.remove(symmetryAxes)
+                canvas3d.remove(symmetryAxes)
             }
         } else {
-            viewer.remove(symmetryAxes)
+            canvas3d.remove(symmetryAxes)
         }
         updated.next(null)
-        viewer.requestDraw(true)
+        canvas3d.requestDraw(true)
     }
 
     await setModel(0, props.assemblyId, props.symmetryFeatureId)
 
     return {
         app,
-        viewer,
+        canvas3d,
 
         get label() { return label },
         models,
@@ -312,12 +312,12 @@ export async function StructureView(app: App, viewer: Canvas3D, models: Readonly
 
         destroy: () => {
             for (const k in structureRepresentations) {
-                viewer.remove(structureRepresentations[k])
+                canvas3d.remove(structureRepresentations[k])
                 structureRepresentations[k].destroy()
             }
-            viewer.remove(polymerSphere)
-            viewer.remove(symmetryAxes)
-            viewer.requestDraw(true)
+            canvas3d.remove(polymerSphere)
+            canvas3d.remove(symmetryAxes)
+            canvas3d.requestDraw(true)
 
             polymerSphere.destroy()
             symmetryAxes.destroy()

+ 1 - 0
src/mol-canvas3d/camera/base.ts

@@ -25,6 +25,7 @@ export const DefaultCameraProps = {
     direction: Vec3.create(0, 0, -1),
     up: Vec3.create(0, 1, 0),
     viewport: Viewport.create(-1, -1, 1, 1),
+    target: Vec3.create(0, 0, 0),
 
     near: 0.1,
     far: 10000,

+ 61 - 0
src/mol-canvas3d/camera/combined.ts

@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { PerspectiveCamera,  } from './perspective';
+import { OrthographicCamera } from './orthographic';
+import { Camera, DefaultCameraProps } from './base';
+import { Vec3 } from 'mol-math/linear-algebra';
+
+// TODO
+
+// updateZoom () {
+//     const fov = degToRad(this.perspectiveCamera.fov)
+//     const height = 2 * Math.tan(fov / 2) * -this.camera.position.z
+//     this.orthographicCamera.zoom = this.height / height
+// }
+
+export type CombinedCameraMode = 'perspective' | 'orthographic'
+
+export interface CombinedCamera extends Camera {
+    target: Vec3
+    fov: number
+    zoom: number
+    mode: CombinedCameraMode
+}
+
+export const DefaultCombinedCameraProps = {
+    ...DefaultCameraProps,
+    target: Vec3.zero(),
+    fov: Math.PI / 4,
+    zoom: 1,
+    mode: 'perspective' as CombinedCameraMode
+}
+export type CombinedCameraProps = Partial<typeof DefaultCombinedCameraProps>
+
+export namespace CombinedCamera {
+    export function create(props: CombinedCameraProps = {}): CombinedCamera {
+        const { zoom, fov, mode, target: t } = { ...DefaultCombinedCameraProps, ...props };
+        const target = Vec3.create(t[0], t[1], t[2])
+        const camera = { ...Camera.create(props), zoom, fov, mode, target }
+        update(camera)
+
+        return camera
+    }
+
+    // const center = Vec3.zero()
+    export function update(camera: CombinedCamera) {
+        // console.log(camera.position, camera.direction)
+        // Vec3.add(center, camera.position, camera.direction)
+
+        const height = 2 * Math.tan(camera.fov / 2) * Vec3.distance(camera.position, camera.target)
+        camera.zoom = camera.viewport.height / height
+
+        switch (camera.mode) {
+            case 'orthographic': OrthographicCamera.update(camera); break
+            case 'perspective': PerspectiveCamera.update(camera); break
+        }
+    }
+}

+ 58 - 1
src/mol-canvas3d/camera/orthographic.ts

@@ -1 +1,58 @@
-// TODO
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Mat4, Vec3 } from 'mol-math/linear-algebra'
+import { DefaultCameraProps, Camera } from './base'
+
+export interface OrthographicCamera extends Camera {
+    zoom: number
+}
+
+export const DefaultOrthographicCameraProps = {
+    ...DefaultCameraProps,
+    zoom: 1
+}
+export type OrthographicCameraProps = Partial<typeof DefaultOrthographicCameraProps>
+
+export namespace OrthographicCamera {
+    export function create(props: OrthographicCameraProps = {}): OrthographicCamera {
+        const { zoom } = { ...DefaultOrthographicCameraProps, ...props };
+        const camera = { ...Camera.create(props), zoom }
+        update(camera)
+
+        return camera
+    }
+
+    const center = Vec3.zero()
+    export function update(camera: OrthographicCamera) {
+        const { viewport, zoom } = camera
+
+        const fullLeft = (viewport.width - viewport.x) / -2
+        const fullRight = (viewport.width - viewport.x) / 2
+        const fullTop = (viewport.height - viewport.y) / 2
+        const fullBottom = (viewport.height - viewport.y) / -2
+
+        const dx = (fullRight - fullLeft) / (2 * zoom)
+        const dy = (fullTop - fullBottom) / (2 * zoom)
+        const cx = (fullRight + fullLeft) / 2
+        const cy = (fullTop + fullBottom) / 2
+
+        const left = cx - dx
+        const right = cx + dx
+        const top = cy + dy
+        const bottom = cy - dy
+
+        // build projection matrix
+        Mat4.ortho(camera.projection, left, right, bottom, top, Math.abs(camera.near), Math.abs(camera.far))
+
+        // build view matrix
+        Vec3.add(center, camera.position, camera.direction)
+        Mat4.lookAt(camera.view, camera.position, center, camera.up)
+
+        // update projection * view and invert
+        Camera.update(camera)
+    }
+}

+ 4 - 9
src/mol-canvas3d/camera/perspective.ts

@@ -8,24 +8,19 @@ import { Mat4, Vec3 } from 'mol-math/linear-algebra'
 import { DefaultCameraProps, Camera } from './base'
 
 export interface PerspectiveCamera extends Camera {
-    fov: number,
+    fov: number
 }
 
 export const DefaultPerspectiveCameraProps = {
+    ...DefaultCameraProps,
     fov: Math.PI / 4,
-    ...DefaultCameraProps
 }
 export type PerspectiveCameraProps = Partial<typeof DefaultPerspectiveCameraProps>
 
 export namespace PerspectiveCamera {
     export function create(props: PerspectiveCameraProps = {}): PerspectiveCamera {
-        let { fov } = { ...DefaultPerspectiveCameraProps, ...props };
-
-        const camera = {
-            ...Camera.create(props),
-            fov
-        }
-
+        const { fov } = { ...DefaultPerspectiveCameraProps, ...props }
+        const camera = { ...Camera.create(props), fov }
         update(camera)
 
         return camera

+ 12 - 4
src/mol-canvas3d/canvas3d.ts

@@ -14,7 +14,6 @@ import { RenderObject } from 'mol-gl/render-object'
 
 import TrackballControls from './controls/trackball'
 import { Viewport } from './camera/util'
-import { PerspectiveCamera } from './camera/perspective'
 import { resizeCanvas } from './util';
 import { createContext, getGLContext, WebGLContext } from 'mol-gl/webgl/context';
 import { Representation } from 'mol-geo/representation';
@@ -25,6 +24,7 @@ import { PickingId, decodeIdRGB } from 'mol-geo/geometry/picking';
 import { MarkerAction } from 'mol-geo/geometry/marker-data';
 import { Loci, EmptyLoci, isEmptyLoci } from 'mol-model/loci';
 import { Color } from 'mol-util/color';
+import { CombinedCamera } from './camera/combined';
 
 interface Canvas3D {
     webgl: WebGLContext,
@@ -53,6 +53,7 @@ interface Canvas3D {
 
     handleResize: () => void
     resetCamera: () => void
+    camera: CombinedCamera
     downloadScreenshot: () => void
     getImageData: (variant: RenderVariant) => ImageData
 
@@ -71,11 +72,16 @@ namespace Canvas3D {
         const didDraw = new BehaviorSubject(0)
         const input = InputObserver.create(canvas)
 
-        const camera = PerspectiveCamera.create({
+        const camera = CombinedCamera.create({
             near: 0.1,
             far: 10000,
-            position: Vec3.create(0, 0, 50)
+            position: Vec3.create(0, 0, 50),
+            mode: 'orthographic'
         })
+        // const camera = OrthographicCamera.create({
+        //     zoom: 8,
+        //     position: Vec3.create(0, 0, 50)
+        // })
         // camera.lookAt(Vec3.create(0, 0, 0))
 
         const gl = getGLContext(canvas, {
@@ -173,7 +179,7 @@ namespace Canvas3D {
             }
             let didRender = false
             controls.update()
-            PerspectiveCamera.update(camera)
+            CombinedCamera.update(camera)
             if (force || !Mat4.areEqual(camera.projectionView, prevProjectionView, EPSILON.Value) || !Mat4.areEqual(scene.view, prevSceneView, EPSILON.Value)) {
                 // console.log('foo', force, prevSceneView, scene.view)
                 Mat4.copy(prevProjectionView, camera.projectionView)
@@ -260,6 +266,7 @@ namespace Canvas3D {
 
             center: (p: Vec3) => {
                 Vec3.set(controls.target, p[0], p[1], p[2])
+                Vec3.set(camera.target, p[0], p[1], p[2])
             },
 
             hide: (repr: Representation<any>) => {
@@ -313,6 +320,7 @@ namespace Canvas3D {
             resetCamera: () => {
                 // TODO
             },
+            camera,
             downloadScreenshot: () => {
                 // TODO
             },

+ 17 - 17
src/mol-math/linear-algebra/3d/mat4.ts

@@ -718,9 +718,9 @@ namespace Mat4 {
      * Generates a frustum matrix with the given bounds
      */
     export function frustum(out: Mat4, left: number, right: number, bottom: number, top: number, near: number, far: number) {
-        let rl = 1 / (right - left);
-        let tb = 1 / (top - bottom);
-        let nf = 1 / (near - far);
+        const rl = 1 / (right - left);
+        const tb = 1 / (top - bottom);
+        const nf = 1 / (near - far);
         out[0] = (near * 2) * rl;
         out[1] = 0;
         out[2] = 0;
@@ -744,8 +744,8 @@ namespace Mat4 {
      * Generates a perspective projection matrix with the given bounds
      */
     export function perspective(out: Mat4, fovy: number, aspect: number, near: number, far: number) {
-        let f = 1.0 / Math.tan(fovy / 2);
-        let nf = 1 / (near - far);
+        const f = 1.0 / Math.tan(fovy / 2);
+        const nf = 1 / (near - far);
         out[0] = f / aspect;
         out[1] = 0;
         out[2] = 0;
@@ -769,9 +769,9 @@ namespace Mat4 {
      * Generates a orthogonal projection matrix with the given bounds
      */
     export function ortho(out: Mat4, left: number, right: number, bottom: number, top: number, near: number, far: number) {
-        let lr = 1 / (left - right);
-        let bt = 1 / (bottom - top);
-        let nf = 1 / (near - far);
+        const lr = 1 / (left - right);
+        const bt = 1 / (bottom - top);
+        const nf = 1 / (near - far);
         out[0] = -2 * lr;
         out[1] = 0;
         out[2] = 0;
@@ -796,15 +796,15 @@ namespace Mat4 {
      */
     export function lookAt(out: Mat4, eye: Vec3, center: Vec3, up: Vec3) {
         let x0, x1, x2, y0, y1, y2, z0, z1, z2, len;
-        let eyex = eye[0];
-        let eyey = eye[1];
-        let eyez = eye[2];
-        let upx = up[0];
-        let upy = up[1];
-        let upz = up[2];
-        let centerx = center[0];
-        let centery = center[1];
-        let centerz = center[2];
+        const eyex = eye[0];
+        const eyey = eye[1];
+        const eyez = eye[2];
+        const upx = up[0];
+        const upy = up[1];
+        const upz = up[2];
+        const centerx = center[0];
+        const centery = center[1];
+        const centerz = center[2];
 
         if (Math.abs(eyex - centerx) < EPSILON.Value &&
             Math.abs(eyey - centery) < EPSILON.Value &&