diff --git a/src/apps/canvas/app.ts b/src/apps/canvas/app.ts index 25b2b5aeba30cd47ab92d99e7fb7d5fcdb46b391..35732c7e5bdd0eeeee726fd313a95a061e1d7c39 100644 --- a/src/apps/canvas/app.ts +++ b/src/apps/canvas/app.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Canvas3D from 'mol-canvas3d/canvas3d'; +import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { getCifFromUrl, getModelsFromMmcif, getCifFromFile, getCcp4FromUrl, getVolumeFromCcp4, getCcp4FromFile, getVolumeFromVolcif } from './util'; import { StructureView } from './structure-view'; import { BehaviorSubject } from 'rxjs'; diff --git a/src/apps/canvas/component/representation.tsx b/src/apps/canvas/component/representation.tsx index 8bbdf8bae91586a7dd784220c1230b24e3b838d8..0e4aedd7bbeee312a4c4a7e95a5ec1048c8aed24 100644 --- a/src/apps/canvas/component/representation.tsx +++ b/src/apps/canvas/component/representation.tsx @@ -5,7 +5,7 @@ */ import * as React from 'react' -import Canvas3D from 'mol-canvas3d/canvas3d'; +import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { App } from '../app'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import { Representation } from 'mol-repr'; diff --git a/src/apps/canvas/structure-view.ts b/src/apps/canvas/structure-view.ts index eeff5581a4cb6d4dcc4e95fe1055642be4122aee..4ba79d8bb50992aed72744b6349f6475c8e52170 100644 --- a/src/apps/canvas/structure-view.ts +++ b/src/apps/canvas/structure-view.ts @@ -8,7 +8,7 @@ import { Model, Structure } from 'mol-model/structure'; import { getStructureFromModel } from './util'; import { AssemblySymmetry } from 'mol-model-props/rcsb/symmetry'; import { getAxesShape } from './assembly-symmetry'; -import Canvas3D from 'mol-canvas3d/canvas3d'; +import { Canvas3D } from 'mol-canvas3d/canvas3d'; // import { MeshBuilder } from 'mol-geo/mesh/mesh-builder'; // import { addSphere } from 'mol-geo/mesh/builder/sphere'; // import { Shape } from 'mol-model/shape'; @@ -217,7 +217,7 @@ export async function StructureView(app: App, canvas3d: Canvas3D, models: Readon } } - canvas3d.center(structure.boundary.sphere.center) + canvas3d.camera.setState({ target: structure.boundary.sphere.center }) // const mb = MeshBuilder.create() // mb.setGroup(0) diff --git a/src/apps/canvas/volume-view.ts b/src/apps/canvas/volume-view.ts index 6c05559ef90422c25527822337f72f7e0e602d02..9d7b8cd1c56372db40d847f429e42a64a7c17861 100644 --- a/src/apps/canvas/volume-view.ts +++ b/src/apps/canvas/volume-view.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Canvas3D from 'mol-canvas3d/canvas3d'; +import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { BehaviorSubject } from 'rxjs'; import { App } from './app'; import { VolumeData } from 'mol-model/volume'; diff --git a/src/examples/task.ts b/src/examples/task.ts index 64143cfb7da152845fa702b6854eb0c5f677c018..0741906538cfd7bedea64365d73a15fa28fbca07 100644 --- a/src/examples/task.ts +++ b/src/examples/task.ts @@ -4,7 +4,8 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Task, Progress, Scheduler, now, MultistepTask, chunkedSubtask } from 'mol-task' +import { Task, Progress, Scheduler, MultistepTask, chunkedSubtask } from 'mol-task' +import { now } from 'mol-util/now'; export async function test1() { const t = Task.create('test', async () => 1); diff --git a/src/mol-canvas3d/camera.ts b/src/mol-canvas3d/camera.ts index 88027f1e5ae4d612e35125b78a2e7581aa71203b..f2e482abc163884b35a6565ec0d160c8de177b67 100644 --- a/src/mol-canvas3d/camera.ts +++ b/src/mol-canvas3d/camera.ts @@ -5,13 +5,18 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Mat4, Vec3, Vec4 } from 'mol-math/linear-algebra' +import { Mat4, Vec3, Vec4, EPSILON } from 'mol-math/linear-algebra' import { Viewport, cameraLookAt, cameraProject, cameraUnproject } from './camera/util'; import { Object3D } from 'mol-gl/object3d'; +import { BehaviorSubject } from 'rxjs'; export { Camera } +// TODO: slab controls that modify near/far planes? + class Camera implements Object3D { + readonly updatedViewProjection = new BehaviorSubject<Camera>(this); + readonly view: Mat4 = Mat4.identity(); readonly projection: Mat4 = Mat4.identity(); readonly projectionView: Mat4 = Mat4.identity(); @@ -32,6 +37,9 @@ class Camera implements Object3D { get target() { return this.state.target; } set target(v: Vec3) { Vec3.copy(this.state.target, v); } + private prevProjection = Mat4.identity(); + private prevView = Mat4.identity(); + updateMatrices() { const snapshot = this.state as Camera.Snapshot; const height = 2 * Math.tan(snapshot.fov / 2) * Vec3.distance(snapshot.position, snapshot.target); @@ -43,8 +51,22 @@ class Camera implements Object3D { default: throw new Error('unknown camera mode'); } + const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON.Value) || !Mat4.areEqual(this.view, this.prevView, EPSILON.Value); + Mat4.mul(this.projectionView, this.projection, this.view) Mat4.invert(this.inverseProjectionView, this.projectionView) + + + if (changed) { + Mat4.mul(this.projectionView, this.projection, this.view) + Mat4.invert(this.inverseProjectionView, this.projectionView) + + Mat4.copy(this.prevView, this.view); + Mat4.copy(this.prevProjection, this.projection); + this.updatedViewProjection.next(this); + } + + return changed; } setState(snapshot?: Partial<Camera.Snapshot>) { @@ -73,6 +95,10 @@ class Camera implements Object3D { return cameraUnproject(out, point, this.viewport, this.inverseProjectionView) } + dispose() { + this.updatedViewProjection.complete(); + } + constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(-1, -1, 1, 1)) { this.viewport = viewport; Camera.copySnapshot(this.state, state); diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 10e2ae36fca38cb2b92958c4ee743deef3e6cc24..3151bab2fc748a979fdc4ee5674850919c04c63a 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -5,8 +5,9 @@ */ import { BehaviorSubject } from 'rxjs'; +import { now } from 'mol-util/now'; -import { Vec3, Mat4, EPSILON } from 'mol-math/linear-algebra' +import { Vec3 } from 'mol-math/linear-algebra' import InputObserver from 'mol-util/input/input-observer' import * as SetUtils from 'mol-util/set' import Renderer, { RendererStats } from 'mol-gl/renderer' @@ -27,17 +28,19 @@ import { Color } from 'mol-util/color'; import { Camera } from './camera'; export const DefaultCanvas3DProps = { + // TODO: FPS cap? + // maxFps: 30, cameraPosition: Vec3.create(0, 0, 50), cameraMode: 'perspective' as Camera.Mode, backgroundColor: Color(0x000000), } export type Canvas3DProps = typeof DefaultCanvas3DProps +export { Canvas3D } + interface Canvas3D { readonly webgl: WebGLContext, - center: (p: Vec3) => void - hide: (repr: Representation<any>) => void show: (repr: Representation<any>) => void @@ -46,7 +49,7 @@ interface Canvas3D { update: () => void clear: () => void - draw: (force?: boolean) => void + // draw: (force?: boolean) => void requestDraw: (force?: boolean) => void animate: () => void pick: () => void @@ -54,9 +57,7 @@ interface Canvas3D { mark: (loci: Loci, action: MarkerAction) => void getLoci: (pickingId: PickingId) => { loci: Loci, repr?: Representation<any> } - readonly reprCount: BehaviorSubject<number> - readonly identified: BehaviorSubject<string> - readonly didDraw: BehaviorSubject<number> + readonly didDraw: BehaviorSubject<now.Timestamp> handleResize: () => void resetCamera: () => void @@ -77,11 +78,9 @@ namespace Canvas3D { const p = { ...props, ...DefaultCanvas3DProps } const reprMap = new Map<Representation<any>, Set<RenderObject>>() - const reprCount = new BehaviorSubject(0) - const identified = new BehaviorSubject('') - const startTime = performance.now() - const didDraw = new BehaviorSubject(0) + const startTime = now() + const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp) const input = InputObserver.create(canvas) const camera = new Camera({ @@ -117,8 +116,6 @@ namespace Canvas3D { let isPicking = false let drawPending = false let lastRenderTime = -1 - const prevProjectionView = Mat4.zero() - const prevSceneView = Mat4.zero() function getLoci(pickingId: PickingId) { let loci: Loci = EmptyLoci @@ -155,7 +152,7 @@ namespace Canvas3D { // return 0 // } - function render(variant: RenderVariant, force?: boolean) { + function render(variant: RenderVariant, force: boolean) { if (isPicking) return false // const p = scene.boundingSphere.center // console.log(p[0], p[1], p[2]) @@ -177,51 +174,56 @@ namespace Canvas3D { // console.log(camera.fogNear, camera.fogFar, targetDistance) - switch (variant) { - case 'pickObject': objectPickTarget.bind(); break; - case 'pickInstance': instancePickTarget.bind(); break; - case 'pickGroup': groupPickTarget.bind(); break; - case 'draw': - webgl.unbindFramebuffer(); - renderer.setViewport(0, 0, canvas.width, canvas.height); - break; - } let didRender = false controls.update() - camera.updateMatrices(); - 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) - Mat4.copy(prevSceneView, scene.view) + const cameraChanged = camera.updateMatrices(); + + if (force || cameraChanged) { + switch (variant) { + case 'pickObject': objectPickTarget.bind(); break; + case 'pickInstance': instancePickTarget.bind(); break; + case 'pickGroup': groupPickTarget.bind(); break; + case 'draw': + webgl.unbindFramebuffer(); + renderer.setViewport(0, 0, canvas.width, canvas.height); + break; + } + renderer.render(scene, variant) if (variant === 'draw') { - lastRenderTime = performance.now() + lastRenderTime = now() pickDirty = true } didRender = true } - return didRender + + return didRender && cameraChanged; } + let forceNextDraw = false; + function draw(force?: boolean) { - if (render('draw', force)) { - didDraw.next(performance.now() - startTime) + if (render('draw', !!force || forceNextDraw)) { + didDraw.next(now() - startTime as now.Timestamp) } + forceNextDraw = false; drawPending = false } function requestDraw(force?: boolean) { if (drawPending) return drawPending = true - window.requestAnimationFrame(() => draw(force)) + forceNextDraw = !!force; + // The animation frame is being requested by animate already. + // window.requestAnimationFrame(() => draw(force)) } function animate() { draw(false) - if (performance.now() - lastRenderTime > 200) { + if (now() - lastRenderTime > 200) { if (pickDirty) pick() } - window.requestAnimationFrame(() => animate()) + window.requestAnimationFrame(animate) } function pick() { @@ -273,11 +275,6 @@ namespace Canvas3D { return { webgl, - center: (target: Vec3) => { - // Vec3.set(controls.target, p[0], p[1], p[2]) - camera.setState({ target }) - }, - hide: (repr: Representation<any>) => { const renderObjectSet = reprMap.get(repr) if (renderObjectSet) renderObjectSet.forEach(o => o.state.visible = false) @@ -299,7 +296,6 @@ namespace Canvas3D { repr.renderObjects.forEach(o => scene.add(o)) } reprMap.set(repr, newRO) - reprCount.next(reprMap.size) scene.update() }, remove: (repr: Representation<any>) => { @@ -307,7 +303,6 @@ namespace Canvas3D { if (renderObjectSet) { renderObjectSet.forEach(o => scene.remove(o)) reprMap.delete(repr) - reprCount.next(reprMap.size) scene.update() } }, @@ -317,7 +312,7 @@ namespace Canvas3D { scene.clear() }, - draw, + // draw, requestDraw, animate, pick, @@ -341,8 +336,6 @@ namespace Canvas3D { case 'pickGroup': return groupPickTarget.getImageData() } }, - reprCount, - identified, didDraw, setProps: (props: Partial<Canvas3DProps>) => { if (props.cameraMode !== undefined && props.cameraMode !== camera.state.mode) { @@ -372,6 +365,7 @@ namespace Canvas3D { input.dispose() controls.dispose() renderer.dispose() + camera.dispose() } } @@ -388,6 +382,4 @@ namespace Canvas3D { groupPickTarget.setSize(pickWidth, pickHeight) } } -} - -export default Canvas3D \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-canvas3d/controls/trackball.ts b/src/mol-canvas3d/controls/trackball.ts index dc2f49ecb6280bf3570a5cd2abf0779720b27f19..9703eac2d106fba7c1e787fd46cf3ab27c625be8 100644 --- a/src/mol-canvas3d/controls/trackball.ts +++ b/src/mol-canvas3d/controls/trackball.ts @@ -25,7 +25,7 @@ export const DefaultTrackballControlsProps = { staticMoving: true, dynamicDampingFactor: 0.2, - minDistance: 0, + minDistance: 0.01, maxDistance: Infinity } export type TrackballControlsProps = Partial<typeof DefaultTrackballControlsProps> @@ -293,7 +293,6 @@ namespace TrackballControls { return { viewport, - // target, get dynamicDampingFactor() { return dynamicDampingFactor }, set dynamicDampingFactor(value: number ) { dynamicDampingFactor = value }, diff --git a/src/mol-model/structure/query/context.ts b/src/mol-model/structure/query/context.ts index 7bc0e6d68093ee0205c84bcf7189233b77ac6246..9dd28a6b265bc17e9c38a32af4839af32fc9ed66 100644 --- a/src/mol-model/structure/query/context.ts +++ b/src/mol-model/structure/query/context.ts @@ -5,7 +5,7 @@ */ import { Structure, StructureElement, Unit } from '../structure'; -import { now } from 'mol-task'; +import { now } from 'mol-util/now'; import { ElementIndex } from '../model'; import { Link } from '../structure/unit/links'; diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 4aca923fe16165ff7a334f495f34b99d227b2839..b519cc8d77643008d90063a49f9e73a9c90c5326 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -5,7 +5,7 @@ */ import { StateTree, StateSelection, Transformer, Transform } from 'mol-state'; -import Canvas3D from 'mol-canvas3d/canvas3d'; +import { Canvas3D } from 'mol-canvas3d/canvas3d'; import { StateTransforms } from './state/transforms'; import { PluginStateObjects as SO } from './state/objects'; import { RxEventHelper } from 'mol-util/rx-event-helper'; @@ -141,8 +141,7 @@ export class PluginContext { if (!sel.length) return; const center = (sel[0].obj! as SO.Structure).data.boundary.sphere.center; - console.log({ sel, center, rc: this.canvas3d.reprCount }); - this.canvas3d.center(center); + this.canvas3d.camera.setState({ target: center }); this.canvas3d.requestDraw(true); } diff --git a/src/mol-task/execution/observable.ts b/src/mol-task/execution/observable.ts index 5b8e82f8991fead4f667dde12a18840ea63ff0d5..5b8d15a07ea63b139192cd74830778c20f4ea79e 100644 --- a/src/mol-task/execution/observable.ts +++ b/src/mol-task/execution/observable.ts @@ -7,7 +7,7 @@ import { Task } from '../task' import { RuntimeContext } from './runtime-context' import { Progress } from './progress' -import { now } from '../util/now' +import { now } from 'mol-util/now'; import { Scheduler } from '../util/scheduler' import { UserTiming } from '../util/user-timing' diff --git a/src/mol-task/index.ts b/src/mol-task/index.ts index 2ef6489c9fffe85d67739274d1aab599dae14de9..62d09e84692b7a2195d41b5295647bdd610d51b7 100644 --- a/src/mol-task/index.ts +++ b/src/mol-task/index.ts @@ -7,9 +7,8 @@ import { Task } from './task' import { RuntimeContext } from './execution/runtime-context' import { Progress } from './execution/progress' -import { now } from './util/now' import { Scheduler } from './util/scheduler' import { MultistepTask } from './util/multistep' import { chunkedSubtask } from './util/chunked' -export { Task, RuntimeContext, Progress, now, Scheduler, MultistepTask, chunkedSubtask } \ No newline at end of file +export { Task, RuntimeContext, Progress, Scheduler, MultistepTask, chunkedSubtask } \ No newline at end of file diff --git a/src/mol-task/util/chunked.ts b/src/mol-task/util/chunked.ts index d091e57066cbda2c53c38d14b0d18a2df53d4c8f..88233dd17a1ded596897308eaf8ae0f31933e5b9 100644 --- a/src/mol-task/util/chunked.ts +++ b/src/mol-task/util/chunked.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { now } from './now' +import { now } from 'mol-util/now'; import { RuntimeContext } from '../execution/runtime-context' type UniformlyChunkedFn<S> = (chunkSize: number, state: S) => number diff --git a/src/mol-task/util/now.ts b/src/mol-util/now.ts similarity index 83% rename from src/mol-task/util/now.ts rename to src/mol-util/now.ts index f4961d217bf3570878d2d0f687c1c19a2f11f8b0..abf35487f174179eb78e9f94589f2333ceaf8772 100644 --- a/src/mol-task/util/now.ts +++ b/src/mol-util/now.ts @@ -7,7 +7,7 @@ declare var process: any; declare var window: any; -const now: () => number = (function () { +const now: () => now.Timestamp = (function () { if (typeof window !== 'undefined' && window.performance) { const perf = window.performance; return () => perf.now(); @@ -23,4 +23,8 @@ const now: () => number = (function () { } }()); +namespace now { + export type Timestamp = number & { '@type': 'now-timestamp' } +} + export { now } \ No newline at end of file diff --git a/src/mol-util/performance-monitor.ts b/src/mol-util/performance-monitor.ts index 614b19a29930bb8eda3424b5e7a5f731acb8493c..615bf93ba0aef9689e84cc820f6a2d11f32caf2e 100644 --- a/src/mol-util/performance-monitor.ts +++ b/src/mol-util/performance-monitor.ts @@ -5,7 +5,7 @@ * Copyright (c) 2016 - now David Sehnal, licensed under Apache 2.0, See LICENSE file for more info. */ -import { now } from 'mol-task/util/now' +import { now } from 'mol-util/now'; export class PerformanceMonitor { private starts = new Map<string, number>(); diff --git a/src/mol-util/uuid.ts b/src/mol-util/uuid.ts index 4c77f7de6120c640cb8a025adf32ffae7429f6a5..7972eaf9fea3c30738f8f97539c1f574e2fd324f 100644 --- a/src/mol-util/uuid.ts +++ b/src/mol-util/uuid.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { now } from 'mol-task' +import { now } from 'mol-util/now'; type UUID = string & { '@type': 'uuid' } diff --git a/src/perf-tests/tasks.ts b/src/perf-tests/tasks.ts index f0c0eb86b3e5948fa3cd061f4bb0c697f861b0ee..068c47b5202691331adfb598924dd363ed798deb 100644 --- a/src/perf-tests/tasks.ts +++ b/src/perf-tests/tasks.ts @@ -1,5 +1,5 @@ import * as B from 'benchmark' -import { now } from 'mol-task/util/now' +import { now } from 'mol-util/now'; import { Scheduler } from 'mol-task/util/scheduler' export namespace Tasks { diff --git a/src/servers/model/preprocess/parallel.ts b/src/servers/model/preprocess/parallel.ts index 2e24f3e0ceb370394aec5990e6e2f20d5a3b2fef..1b600b5b031313b63827de07d5a13992e541974d 100644 --- a/src/servers/model/preprocess/parallel.ts +++ b/src/servers/model/preprocess/parallel.ts @@ -6,7 +6,7 @@ import * as path from 'path' import * as cluster from 'cluster' -import { now } from 'mol-task'; +import { now } from 'mol-util/now'; import { PerformanceMonitor } from 'mol-util/performance-monitor'; import { preprocessFile } from './preprocess'; import { createModelPropertiesProvider } from '../property-provider'; diff --git a/src/servers/model/server/api-local.ts b/src/servers/model/server/api-local.ts index f1d5e2f1c213690c586d518208d58d0231aac139..83fe022eb141e6fbf4ddeeaa30d15781978b19a4 100644 --- a/src/servers/model/server/api-local.ts +++ b/src/servers/model/server/api-local.ts @@ -10,7 +10,7 @@ import { JobManager, Job } from './jobs'; import { ConsoleLogger } from 'mol-util/console-logger'; import { resolveJob } from './query'; import { StructureCache } from './structure-wrapper'; -import { now } from 'mol-task'; +import { now } from 'mol-util/now'; import { PerformanceMonitor } from 'mol-util/performance-monitor'; import { QueryName } from './api'; diff --git a/src/servers/model/server/query.ts b/src/servers/model/server/query.ts index 0c80ae56ccf240290d028d7424dabd16dc7a86ad..f1079596487273caec14432f1fbbc0afc7274063 100644 --- a/src/servers/model/server/query.ts +++ b/src/servers/model/server/query.ts @@ -8,7 +8,8 @@ import { Column } from 'mol-data/db'; import { CifWriter } from 'mol-io/writer/cif'; import { StructureQuery, StructureSelection, Structure } from 'mol-model/structure'; import { encode_mmCIF_categories } from 'mol-model/structure/export/mmcif'; -import { now, Progress } from 'mol-task'; +import { Progress } from 'mol-task'; +import { now } from 'mol-util/now'; import { ConsoleLogger } from 'mol-util/console-logger'; import { PerformanceMonitor } from 'mol-util/performance-monitor'; import Config from '../config'; diff --git a/src/servers/model/utils/fetch-props-pdbe.ts b/src/servers/model/utils/fetch-props-pdbe.ts index f28d9946b62a9f111a30184649298e4042349fd9..76d77f662c7b799ccd6cea58a1143a3880be40e2 100644 --- a/src/servers/model/utils/fetch-props-pdbe.ts +++ b/src/servers/model/utils/fetch-props-pdbe.ts @@ -9,7 +9,7 @@ import * as fs from 'fs' import * as path from 'path' import * as argparse from 'argparse' import { makeDir } from 'mol-util/make-dir'; -import { now } from 'mol-task'; +import { now } from 'mol-util/now'; import { PerformanceMonitor } from 'mol-util/performance-monitor'; const cmdParser = new argparse.ArgumentParser({