|
@@ -2,6 +2,7 @@
|
|
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
*
|
|
*
|
|
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
|
+ * @author David Sehnal <david.sehnal@gmail.com>
|
|
*/
|
|
*/
|
|
|
|
|
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
|
@@ -56,30 +57,33 @@ export { Canvas3D }
|
|
interface Canvas3D {
|
|
interface Canvas3D {
|
|
readonly webgl: WebGLContext,
|
|
readonly webgl: WebGLContext,
|
|
|
|
|
|
- add: (repr: Representation.Any) => Promise<void>
|
|
|
|
- remove: (repr: Representation.Any) => Promise<void>
|
|
|
|
- update: (repr?: Representation.Any, keepBoundingSphere?: boolean) => void
|
|
|
|
- clear: () => void
|
|
|
|
-
|
|
|
|
- // draw: (force?: boolean) => void
|
|
|
|
- requestDraw: (force?: boolean) => void
|
|
|
|
- animate: () => void
|
|
|
|
- identify: (x: number, y: number) => PickingId | undefined
|
|
|
|
- mark: (loci: Representation.Loci, action: MarkerAction) => void
|
|
|
|
- getLoci: (pickingId: PickingId) => Representation.Loci
|
|
|
|
|
|
+ add(repr: Representation.Any): void
|
|
|
|
+ remove(repr: Representation.Any): void
|
|
|
|
+ /**
|
|
|
|
+ * This function must be called if animate() is not set up so that add/remove actions take place.
|
|
|
|
+ */
|
|
|
|
+ commit(): void
|
|
|
|
+ update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
|
|
|
|
+ clear(): void
|
|
|
|
+
|
|
|
|
+ requestDraw(force?: boolean): void
|
|
|
|
+ animate(): void
|
|
|
|
+ identify(x: number, y: number): PickingId | undefined
|
|
|
|
+ mark(loci: Representation.Loci, action: MarkerAction): void
|
|
|
|
+ getLoci(pickingId: PickingId): Representation.Loci
|
|
|
|
|
|
readonly didDraw: BehaviorSubject<now.Timestamp>
|
|
readonly didDraw: BehaviorSubject<now.Timestamp>
|
|
readonly reprCount: BehaviorSubject<number>
|
|
readonly reprCount: BehaviorSubject<number>
|
|
|
|
|
|
- handleResize: () => void
|
|
|
|
|
|
+ handleResize(): void
|
|
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
|
|
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
|
|
- resetCamera: () => void
|
|
|
|
|
|
+ requestCameraReset(): void
|
|
readonly camera: Camera
|
|
readonly camera: Camera
|
|
readonly boundingSphere: Readonly<Sphere3D>
|
|
readonly boundingSphere: Readonly<Sphere3D>
|
|
- downloadScreenshot: () => void
|
|
|
|
- getPixelData: (variant: GraphicsRenderVariant) => PixelData
|
|
|
|
- setProps: (props: Partial<Canvas3DProps>) => void
|
|
|
|
- getImagePass: () => ImagePass
|
|
|
|
|
|
+ downloadScreenshot(): void
|
|
|
|
+ getPixelData(variant: GraphicsRenderVariant): PixelData
|
|
|
|
+ setProps(props: Partial<Canvas3DProps>): void
|
|
|
|
+ getImagePass(): ImagePass
|
|
|
|
|
|
/** Returns a copy of the current Canvas3D instance props */
|
|
/** Returns a copy of the current Canvas3D instance props */
|
|
readonly props: Readonly<Canvas3DProps>
|
|
readonly props: Readonly<Canvas3DProps>
|
|
@@ -87,7 +91,7 @@ interface Canvas3D {
|
|
readonly stats: RendererStats
|
|
readonly stats: RendererStats
|
|
readonly interaction: Canvas3dInteractionHelper['events']
|
|
readonly interaction: Canvas3dInteractionHelper['events']
|
|
|
|
|
|
- dispose: () => void
|
|
|
|
|
|
+ dispose(): void
|
|
}
|
|
}
|
|
|
|
|
|
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
|
|
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
|
|
@@ -220,7 +224,7 @@ namespace Canvas3D {
|
|
}
|
|
}
|
|
|
|
|
|
function render(force: boolean) {
|
|
function render(force: boolean) {
|
|
- if (scene.isCommiting || webgl.isContextLost) return false
|
|
|
|
|
|
+ if (webgl.isContextLost) return false
|
|
|
|
|
|
let didRender = false
|
|
let didRender = false
|
|
controls.update(currentTime)
|
|
controls.update(currentTime)
|
|
@@ -262,7 +266,11 @@ namespace Canvas3D {
|
|
|
|
|
|
function animate() {
|
|
function animate() {
|
|
currentTime = now();
|
|
currentTime = now();
|
|
|
|
+
|
|
|
|
+ commit();
|
|
|
|
+
|
|
camera.transition.tick(currentTime);
|
|
camera.transition.tick(currentTime);
|
|
|
|
+
|
|
draw(false);
|
|
draw(false);
|
|
if (!camera.transition.inTransition && !webgl.isContextLost) {
|
|
if (!camera.transition.inTransition && !webgl.isContextLost) {
|
|
interactionHelper.tick(currentTime);
|
|
interactionHelper.tick(currentTime);
|
|
@@ -274,22 +282,31 @@ namespace Canvas3D {
|
|
return webgl.isContextLost ? undefined : pickPass.identify(x, y)
|
|
return webgl.isContextLost ? undefined : pickPass.identify(x, y)
|
|
}
|
|
}
|
|
|
|
|
|
- async function commit(renderObjects?: readonly GraphicsRenderObject[]) {
|
|
|
|
- scene.update(renderObjects, false)
|
|
|
|
|
|
+ function commit() {
|
|
|
|
+ commitScene();
|
|
|
|
+ resolveCameraReset();
|
|
|
|
+ }
|
|
|
|
|
|
- return runTask(scene.commit()).then(() => {
|
|
|
|
- if (cameraResetRequested && !scene.isCommiting) {
|
|
|
|
- const { center, radius } = scene.boundingSphere
|
|
|
|
- camera.focus(center, radius, radius)
|
|
|
|
- cameraResetRequested = false
|
|
|
|
- }
|
|
|
|
- if (debugHelper.isEnabled) debugHelper.update()
|
|
|
|
- requestDraw(true)
|
|
|
|
- reprCount.next(reprRenderObjects.size)
|
|
|
|
- })
|
|
|
|
|
|
+ function resolveCameraReset() {
|
|
|
|
+ if (!cameraResetRequested) return;
|
|
|
|
+ const { center, radius } = scene.boundingSphere;
|
|
|
|
+ camera.focus(center, radius, radius, p.cameraResetDurationMs);
|
|
|
|
+ cameraResetRequested = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let isDirty = false;
|
|
|
|
+ function commitScene() {
|
|
|
|
+ if (!isDirty) return;
|
|
|
|
+
|
|
|
|
+ scene.syncCommit();
|
|
|
|
+ if (debugHelper.isEnabled) debugHelper.update();
|
|
|
|
+ reprCount.next(reprRenderObjects.size);
|
|
|
|
+ isDirty = false;
|
|
}
|
|
}
|
|
|
|
|
|
function add(repr: Representation.Any) {
|
|
function add(repr: Representation.Any) {
|
|
|
|
+ registerAutoUpdate(repr);
|
|
|
|
+
|
|
const oldRO = reprRenderObjects.get(repr)
|
|
const oldRO = reprRenderObjects.get(repr)
|
|
const newRO = new Set<GraphicsRenderObject>()
|
|
const newRO = new Set<GraphicsRenderObject>()
|
|
repr.renderObjects.forEach(o => newRO.add(o))
|
|
repr.renderObjects.forEach(o => newRO.add(o))
|
|
@@ -303,7 +320,37 @@ namespace Canvas3D {
|
|
repr.renderObjects.forEach(o => scene.add(o))
|
|
repr.renderObjects.forEach(o => scene.add(o))
|
|
}
|
|
}
|
|
reprRenderObjects.set(repr, newRO)
|
|
reprRenderObjects.set(repr, newRO)
|
|
- return commit(repr.renderObjects)
|
|
|
|
|
|
+
|
|
|
|
+ scene.update(repr.renderObjects, false)
|
|
|
|
+ isDirty = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function remove(repr: Representation.Any) {
|
|
|
|
+ unregisterAutoUpdate(repr);
|
|
|
|
+
|
|
|
|
+ const renderObjects = reprRenderObjects.get(repr)
|
|
|
|
+ if (renderObjects) {
|
|
|
|
+ renderObjects.forEach(o => scene.remove(o))
|
|
|
|
+ reprRenderObjects.delete(repr)
|
|
|
|
+ scene.update(repr.renderObjects, false, true)
|
|
|
|
+ isDirty = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function registerAutoUpdate(repr: Representation.Any) {
|
|
|
|
+ if (reprUpdatedSubscriptions.has(repr)) return;
|
|
|
|
+
|
|
|
|
+ reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
|
|
|
|
+ if (!repr.state.syncManually) add(repr);
|
|
|
|
+ }))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function unregisterAutoUpdate(repr: Representation.Any) {
|
|
|
|
+ const updatedSubscription = reprUpdatedSubscriptions.get(repr);
|
|
|
|
+ if (updatedSubscription) {
|
|
|
|
+ updatedSubscription.unsubscribe();
|
|
|
|
+ reprUpdatedSubscriptions.delete(repr);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
handleResize()
|
|
handleResize()
|
|
@@ -311,25 +358,9 @@ namespace Canvas3D {
|
|
return {
|
|
return {
|
|
webgl,
|
|
webgl,
|
|
|
|
|
|
- add: (repr: Representation.Any) => {
|
|
|
|
- reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
|
|
|
|
- if (!repr.state.syncManually) add(repr)
|
|
|
|
- }))
|
|
|
|
- return add(repr)
|
|
|
|
- },
|
|
|
|
- remove: (repr: Representation.Any) => {
|
|
|
|
- const updatedSubscription = reprUpdatedSubscriptions.get(repr)
|
|
|
|
- if (updatedSubscription) {
|
|
|
|
- updatedSubscription.unsubscribe()
|
|
|
|
- }
|
|
|
|
- const renderObjects = reprRenderObjects.get(repr)
|
|
|
|
- if (renderObjects) {
|
|
|
|
- renderObjects.forEach(o => scene.remove(o))
|
|
|
|
- reprRenderObjects.delete(repr)
|
|
|
|
- return commit()
|
|
|
|
- }
|
|
|
|
- return Promise.resolve()
|
|
|
|
- },
|
|
|
|
|
|
+ add,
|
|
|
|
+ remove,
|
|
|
|
+ commit,
|
|
update: (repr, keepSphere) => {
|
|
update: (repr, keepSphere) => {
|
|
if (repr) {
|
|
if (repr) {
|
|
if (!reprRenderObjects.has(repr)) return;
|
|
if (!reprRenderObjects.has(repr)) return;
|
|
@@ -356,14 +387,8 @@ namespace Canvas3D {
|
|
getLoci,
|
|
getLoci,
|
|
|
|
|
|
handleResize,
|
|
handleResize,
|
|
- resetCamera: () => {
|
|
|
|
- if (scene.isCommiting) {
|
|
|
|
- cameraResetRequested = true
|
|
|
|
- } else {
|
|
|
|
- const { center, radius } = scene.boundingSphere
|
|
|
|
- camera.focus(center, radius, radius, p.cameraResetDurationMs)
|
|
|
|
- requestDraw(true);
|
|
|
|
- }
|
|
|
|
|
|
+ requestCameraReset: () => {
|
|
|
|
+ cameraResetRequested = true;
|
|
},
|
|
},
|
|
camera,
|
|
camera,
|
|
boundingSphere: scene.boundingSphere,
|
|
boundingSphere: scene.boundingSphere,
|