UNPKG

molstar

Version:

A comprehensive macromolecular library.

812 lines (811 loc) 39.6 kB
/** * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> * @author Gianluca Tomasello <giagitom@gmail.com> */ import { __assign } from "tslib"; import { BehaviorSubject } from 'rxjs'; import { now } from '../mol-util/now'; import { Vec3 } from '../mol-math/linear-algebra'; import { InputObserver } from '../mol-util/input/input-observer'; import { Renderer, RendererParams } from '../mol-gl/renderer'; import { TrackballControls, TrackballControlsParams } from './controls/trackball'; import { Viewport } from './camera/util'; import { createContext, getGLContext } from '../mol-gl/webgl/context'; import { Representation } from '../mol-repr/representation'; import { Scene } from '../mol-gl/scene'; import { EmptyLoci, isEmptyLoci } from '../mol-model/loci'; import { Camera } from './camera'; import { ParamDefinition as PD } from '../mol-util/param-definition'; import { DebugHelperParams } from './helper/bounding-sphere-helper'; import { SetUtils } from '../mol-util/set'; import { Canvas3dInteractionHelper, Canvas3dInteractionHelperParams } from './helper/interaction-events'; import { PostprocessingParams } from './passes/postprocessing'; import { MultiSampleHelper, MultiSampleParams, MultiSamplePass } from './passes/multi-sample'; import { PickHelper } from './passes/pick'; import { ImagePass } from './passes/image'; import { Sphere3D } from '../mol-math/geometry'; import { isDebugMode, isTimingMode } from '../mol-util/debug'; import { CameraHelperParams } from './helper/camera-helper'; import { produce } from 'immer'; import { HandleHelperParams } from './helper/handle-helper'; import { StereoCamera, StereoCameraParams } from './camera/stereo'; import { Helper } from './helper/helper'; import { Passes } from './passes/passes'; import { shallowEqual } from '../mol-util'; import { MarkingParams } from './passes/marking'; import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit, GraphicsRenderVariantsDpoit } from '../mol-gl/webgl/render-item'; import { degToRad, radToDeg } from '../mol-math/misc'; import { deepClone } from '../mol-util/object'; export var Canvas3DParams = { camera: PD.Group({ mode: PD.Select('perspective', PD.arrayToOptions(['perspective', 'orthographic']), { label: 'Camera' }), helper: PD.Group(CameraHelperParams, { isFlat: true }), stereo: PD.MappedStatic('off', { on: PD.Group(StereoCameraParams), off: PD.Group({}) }, { cycle: true, hideIf: function (p) { return (p === null || p === void 0 ? void 0 : p.mode) !== 'perspective'; } }), fov: PD.Numeric(45, { min: 10, max: 130, step: 1 }, { label: 'Field of View' }), manualReset: PD.Boolean(false, { isHidden: true }), }, { pivot: 'mode' }), cameraFog: PD.MappedStatic('on', { on: PD.Group({ intensity: PD.Numeric(15, { min: 1, max: 100, step: 1 }), }), off: PD.Group({}) }, { cycle: true, description: 'Show fog in the distance' }), cameraClipping: PD.Group({ radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }), far: PD.Boolean(true, { description: 'Hide scene in the distance' }), minNear: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }), }, { pivot: 'radius' }), viewport: PD.MappedStatic('canvas', { canvas: PD.Group({}), 'static-frame': PD.Group({ x: PD.Numeric(0), y: PD.Numeric(0), width: PD.Numeric(128), height: PD.Numeric(128) }), 'relative-frame': PD.Group({ x: PD.Numeric(0.33, { min: 0, max: 1, step: 0.01 }), y: PD.Numeric(0.33, { min: 0, max: 1, step: 0.01 }), width: PD.Numeric(0.5, { min: 0.01, max: 1, step: 0.01 }), height: PD.Numeric(0.5, { min: 0.01, max: 1, step: 0.01 }) }) }), cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }), sceneRadiusFactor: PD.Numeric(1, { min: 1, max: 10, step: 0.1 }), transparentBackground: PD.Boolean(false), dpoitIterations: PD.Numeric(2, { min: 1, max: 10, step: 1 }), multiSample: PD.Group(MultiSampleParams), postprocessing: PD.Group(PostprocessingParams), marking: PD.Group(MarkingParams), renderer: PD.Group(RendererParams), trackball: PD.Group(TrackballControlsParams), interaction: PD.Group(Canvas3dInteractionHelperParams), debug: PD.Group(DebugHelperParams), handle: PD.Group(HandleHelperParams), }; export var DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams); export { Canvas3DContext }; var Canvas3DContext; (function (Canvas3DContext) { Canvas3DContext.DefaultAttribs = { failIfMajorPerformanceCaveat: false, /** true by default to avoid issues with Safari (Jan 2021) */ antialias: true, /** true to support multiple Canvas3D objects with a single context */ preserveDrawingBuffer: true, pixelScale: 1, pickScale: 0.25, /** extra pixels to around target to check in case target is empty */ pickPadding: 1, enableWboit: true, enableDpoit: false, preferWebGl1: false }; function fromCanvas(canvas, assetManager, attribs) { if (attribs === void 0) { attribs = {}; } var a = __assign(__assign({}, Canvas3DContext.DefaultAttribs), attribs); if (a.enableWboit && a.enableDpoit) throw new Error('Multiple transparency methods not allowed.'); var failIfMajorPerformanceCaveat = a.failIfMajorPerformanceCaveat, antialias = a.antialias, preserveDrawingBuffer = a.preserveDrawingBuffer, pixelScale = a.pixelScale, preferWebGl1 = a.preferWebGl1; var gl = getGLContext(canvas, { failIfMajorPerformanceCaveat: failIfMajorPerformanceCaveat, antialias: antialias, preserveDrawingBuffer: preserveDrawingBuffer, alpha: true, depth: true, premultipliedAlpha: true, preferWebGl1: preferWebGl1 }); if (gl === null) throw new Error('Could not create a WebGL rendering context'); var input = InputObserver.fromElement(canvas, { pixelScale: pixelScale, preventGestures: true }); var webgl = createContext(gl, { pixelScale: pixelScale }); var passes = new Passes(webgl, assetManager, a); if (isDebugMode) { var loseContextExt_1 = gl.getExtension('WEBGL_lose_context'); if (loseContextExt_1) { // Hold down shift+ctrl+alt and press any mouse button to call `loseContext`. // After 1 second `restoreContext` will be called. canvas.addEventListener('mousedown', function (e) { if (webgl.isContextLost) return; if (!e.shiftKey || !e.ctrlKey || !e.altKey) return; if (isDebugMode) console.log('lose context'); loseContextExt_1.loseContext(); setTimeout(function () { if (!webgl.isContextLost) return; if (isDebugMode) console.log('restore context'); loseContextExt_1.restoreContext(); }, 1000); }, false); } } // https://www.khronos.org/webgl/wiki/HandlingContextLost var contextLost = new BehaviorSubject(0); var handleWebglContextLost = function (e) { webgl.setContextLost(); e.preventDefault(); if (isDebugMode) console.log('context lost'); contextLost.next(now()); }; var handlewWebglContextRestored = function () { if (!webgl.isContextLost) return; webgl.handleContextRestored(function () { passes.draw.reset(); }); if (isDebugMode) console.log('context restored'); }; canvas.addEventListener('webglcontextlost', handleWebglContextLost, false); canvas.addEventListener('webglcontextrestored', handlewWebglContextRestored, false); return { canvas: canvas, webgl: webgl, input: input, passes: passes, attribs: a, contextLost: contextLost, contextRestored: webgl.contextRestored, assetManager: assetManager, dispose: function (options) { input.dispose(); canvas.removeEventListener('webglcontextlost', handleWebglContextLost, false); canvas.removeEventListener('webglcontextrestored', handlewWebglContextRestored, false); webgl.destroy(options); } }; } Canvas3DContext.fromCanvas = fromCanvas; })(Canvas3DContext || (Canvas3DContext = {})); export { Canvas3D }; var requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : function (f) { return setImmediate(function () { return f(Date.now()); }); }; var cancelAnimationFrame = typeof window !== 'undefined' ? window.cancelAnimationFrame : function (handle) { return clearImmediate(handle); }; var Canvas3D; (function (Canvas3D) { function create(_a, props) { var webgl = _a.webgl, input = _a.input, passes = _a.passes, attribs = _a.attribs, assetManager = _a.assetManager; if (props === void 0) { props = {}; } var p = __assign(__assign({}, deepClone(DefaultCanvas3DParams)), deepClone(props)); var reprRenderObjects = new Map(); var reprUpdatedSubscriptions = new Map(); var reprCount = new BehaviorSubject(0); var startTime = now(); var didDraw = new BehaviorSubject(0); var commited = new BehaviorSubject(0); var gl = webgl.gl, contextRestored = webgl.contextRestored; var x = 0; var y = 0; var width = 128; var height = 128; updateViewport(); var scene = Scene.create(webgl, passes.draw.dpoitEnabled ? GraphicsRenderVariantsDpoit : (passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended)); function getSceneRadius() { return scene.boundingSphere.radius * p.sceneRadiusFactor; } var camera = new Camera({ position: Vec3.create(0, 0, 100), mode: p.camera.mode, fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0, clipFar: p.cameraClipping.far, minNear: p.cameraClipping.minNear, fov: degToRad(p.camera.fov), }, { x: x, y: y, width: width, height: height }, { pixelScale: attribs.pixelScale }); var stereoCamera = new StereoCamera(camera, p.camera.stereo.params); var controls = TrackballControls.create(input, camera, p.trackball); var renderer = Renderer.create(webgl, p.renderer); var helper = new Helper(webgl, scene, p); var pickHelper = new PickHelper(webgl, renderer, scene, helper, passes.pick, { x: x, y: y, width: width, height: height }, attribs.pickPadding); var interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera, p.interaction); var multiSampleHelper = new MultiSampleHelper(passes.multiSample); passes.draw.postprocessing.background.update(camera, p.postprocessing.background, function (changed) { if (changed) requestDraw(); }); var cameraResetRequested = false; var nextCameraResetDuration = void 0; var nextCameraResetSnapshot = void 0; var resizeRequested = false; var notifyDidDraw = true; function getLoci(pickingId) { var loci = EmptyLoci; var repr = Representation.Empty; if (pickingId) { var cameraHelperLoci = helper.camera.getLoci(pickingId); if (cameraHelperLoci !== EmptyLoci) return { loci: cameraHelperLoci, repr: repr }; loci = helper.handle.getLoci(pickingId); reprRenderObjects.forEach(function (_, _repr) { var _loci = _repr.getLoci(pickingId); if (!isEmptyLoci(_loci)) { if (!isEmptyLoci(loci)) { console.warn('found another loci, this should not happen'); } loci = _loci; repr = _repr; } }); } return { loci: loci, repr: repr }; } var markBuffer = []; function mark(reprLoci, action) { // NOTE: might try to optimize a case with opposite actions for the // same loci. Tho this might end up being more expensive (and error prone) // then just applying everything "naively". markBuffer.push([reprLoci, action]); } function resolveMarking() { var changed = false; for (var _i = 0, markBuffer_1 = markBuffer; _i < markBuffer_1.length; _i++) { var _a = markBuffer_1[_i], r = _a[0], l = _a[1]; changed = applyMark(r, l) || changed; } markBuffer = []; if (changed) { scene.update(void 0, true); helper.handle.scene.update(void 0, true); helper.camera.scene.update(void 0, true); } return changed; } function applyMark(reprLoci, action) { var repr = reprLoci.repr, loci = reprLoci.loci; var changed = false; if (repr) { changed = repr.mark(loci, action) || changed; } else { reprRenderObjects.forEach(function (_, _repr) { changed = _repr.mark(loci, action) || changed; }); } changed = helper.handle.mark(loci, action) || changed; changed = helper.camera.mark(loci, action) || changed; return changed; } function render(force) { if (webgl.isContextLost) return false; var resized = false; if (resizeRequested) { handleResize(false); resizeRequested = false; resized = true; } if (x > gl.drawingBufferWidth || x + width < 0 || y > gl.drawingBufferHeight || y + height < 0) return false; var markingUpdated = resolveMarking() && (renderer.props.colorMarker || p.marking.enabled); var didRender = false; controls.update(currentTime); var cameraChanged = camera.update(); var shouldRender = force || cameraChanged || resized || forceNextRender; forceNextRender = false; var multiSampleChanged = multiSampleHelper.update(markingUpdated || shouldRender, p.multiSample); if (shouldRender || multiSampleChanged || markingUpdated) { var cam = camera; if (p.camera.stereo.name === 'on') { stereoCamera.update(); cam = stereoCamera; } if (isTimingMode) webgl.timer.mark('Canvas3D.render'); var ctx = { renderer: renderer, camera: cam, scene: scene, helper: helper }; if (MultiSamplePass.isEnabled(p.multiSample)) { var forceOn = !cameraChanged && markingUpdated && !controls.isAnimating; multiSampleHelper.render(ctx, p, true, forceOn); } else { passes.draw.render(ctx, p, true); } if (isTimingMode) webgl.timer.markEnd('Canvas3D.render'); // if only marking has updated, do not set the flag to dirty pickHelper.dirty = pickHelper.dirty || shouldRender; didRender = true; } return didRender; } var forceNextRender = false; var forceDrawAfterAllCommited = false; var currentTime = 0; var drawPaused = false; function draw(options) { if (drawPaused) return; if (render(!!(options === null || options === void 0 ? void 0 : options.force)) && notifyDidDraw) { didDraw.next(now() - startTime); } } function requestDraw() { forceNextRender = true; } var animationFrameHandle = 0; function tick(t, options) { currentTime = t; commit(options === null || options === void 0 ? void 0 : options.isSynchronous); camera.transition.tick(currentTime); if (options === null || options === void 0 ? void 0 : options.manualDraw) { return; } draw(); if (!camera.transition.inTransition && !webgl.isContextLost) { interactionHelper.tick(currentTime); } } function _animate() { tick(now()); animationFrameHandle = requestAnimationFrame(_animate); } function resetTime(t) { startTime = t; controls.start(t); } function animate() { drawPaused = false; controls.start(now()); if (animationFrameHandle === 0) _animate(); } function pause(noDraw) { if (noDraw === void 0) { noDraw = false; } drawPaused = noDraw; cancelAnimationFrame(animationFrameHandle); animationFrameHandle = 0; } function identify(x, y) { var cam = p.camera.stereo.name === 'on' ? stereoCamera : camera; return webgl.isContextLost ? undefined : pickHelper.identify(x, y, cam); } function commit(isSynchronous) { if (isSynchronous === void 0) { isSynchronous = false; } var allCommited = commitScene(isSynchronous); // Only reset the camera after the full scene has been commited. if (allCommited) { resolveCameraReset(); if (forceDrawAfterAllCommited) { if (helper.debug.isEnabled) helper.debug.update(); draw({ force: true }); forceDrawAfterAllCommited = false; } commited.next(now()); } } function resolveCameraReset() { if (!cameraResetRequested) return; var boundingSphere = scene.boundingSphereVisible; var center = boundingSphere.center, radius = boundingSphere.radius; var autoAdjustControls = controls.props.autoAdjustMinMaxDistance; if (autoAdjustControls.name === 'on') { var minDistance = autoAdjustControls.params.minDistanceFactor * radius + autoAdjustControls.params.minDistancePadding; var maxDistance = Math.max(autoAdjustControls.params.maxDistanceFactor * radius, autoAdjustControls.params.maxDistanceMin); controls.setProps({ minDistance: minDistance, maxDistance: maxDistance }); } if (radius > 0) { var duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration; var focus_1 = camera.getFocus(center, radius); var next = typeof nextCameraResetSnapshot === 'function' ? nextCameraResetSnapshot(scene, camera) : nextCameraResetSnapshot; var snapshot = next ? __assign(__assign({}, focus_1), next) : focus_1; camera.setState(__assign(__assign({}, snapshot), { radiusMax: getSceneRadius() }), duration); } nextCameraResetDuration = void 0; nextCameraResetSnapshot = void 0; cameraResetRequested = false; } var oldBoundingSphereVisible = Sphere3D(); var cameraSphere = Sphere3D(); function shouldResetCamera() { if (camera.state.radiusMax === 0) return true; if (camera.transition.inTransition || nextCameraResetSnapshot) return false; var cameraSphereOverlapsNone = true, isEmpty = true; Sphere3D.set(cameraSphere, camera.state.target, camera.state.radius); // check if any renderable has moved outside of the old bounding sphere // and if no renderable is overlapping with the camera sphere for (var _i = 0, _a = scene.renderables; _i < _a.length; _i++) { var r = _a[_i]; if (!r.state.visible) continue; var b = r.values.boundingSphere.ref.value; if (!b.radius) continue; isEmpty = false; var cameraDist = Vec3.distance(cameraSphere.center, b.center); if ((cameraDist > cameraSphere.radius || cameraDist > b.radius || b.radius > camera.state.radiusMax) && !Sphere3D.includes(oldBoundingSphereVisible, b)) return true; if (Sphere3D.overlaps(cameraSphere, b)) cameraSphereOverlapsNone = false; } return cameraSphereOverlapsNone || (!isEmpty && cameraSphere.radius <= 0.1); } var sceneCommitTimeoutMs = 250; function commitScene(isSynchronous) { if (!scene.needsCommit) return true; // snapshot the current bounding sphere of visible objects Sphere3D.copy(oldBoundingSphereVisible, scene.boundingSphereVisible); if (!scene.commit(isSynchronous ? void 0 : sceneCommitTimeoutMs)) return false; if (helper.debug.isEnabled) helper.debug.update(); if (!p.camera.manualReset && (reprCount.value === 0 || shouldResetCamera())) { cameraResetRequested = true; } if (oldBoundingSphereVisible.radius === 0) nextCameraResetDuration = 0; if (!p.camera.manualReset) camera.setState({ radiusMax: getSceneRadius() }, 0); reprCount.next(reprRenderObjects.size); if (isDebugMode) consoleStats(); return true; } function consoleStats() { console.table(scene.renderables.map(function (r) { return ({ drawCount: r.values.drawCount.ref.value, instanceCount: r.values.instanceCount.ref.value, materialId: r.materialId, renderItemId: r.id, }); })); console.log(webgl.stats); var _a = webgl.resources.getByteCounts(), texture = _a.texture, attribute = _a.attribute, elements = _a.elements; console.log({ texture: "".concat((texture / 1024 / 1024).toFixed(3), " MiB"), attribute: "".concat((attribute / 1024 / 1024).toFixed(3), " MiB"), elements: "".concat((elements / 1024 / 1024).toFixed(3), " MiB"), }); } function add(repr) { registerAutoUpdate(repr); var oldRO = reprRenderObjects.get(repr); var newRO = new Set(); repr.renderObjects.forEach(function (o) { return newRO.add(o); }); if (oldRO) { if (!SetUtils.areEqual(newRO, oldRO)) { newRO.forEach(function (o) { if (!oldRO.has(o)) scene.add(o); }); oldRO.forEach(function (o) { if (!newRO.has(o)) scene.remove(o); }); } } else { repr.renderObjects.forEach(function (o) { return scene.add(o); }); } reprRenderObjects.set(repr, newRO); scene.update(repr.renderObjects, false); forceDrawAfterAllCommited = true; if (isDebugMode) consoleStats(); } function remove(repr) { unregisterAutoUpdate(repr); var renderObjects = reprRenderObjects.get(repr); if (renderObjects) { renderObjects.forEach(function (o) { return scene.remove(o); }); reprRenderObjects.delete(repr); forceDrawAfterAllCommited = true; if (isDebugMode) consoleStats(); } } function registerAutoUpdate(repr) { if (reprUpdatedSubscriptions.has(repr)) return; reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(function (_) { if (!repr.state.syncManually) add(repr); })); } function unregisterAutoUpdate(repr) { var updatedSubscription = reprUpdatedSubscriptions.get(repr); if (updatedSubscription) { updatedSubscription.unsubscribe(); reprUpdatedSubscriptions.delete(repr); } } function getProps() { var radius = scene.boundingSphere.radius > 0 ? 100 - Math.round((camera.transition.target.radius / getSceneRadius()) * 100) : 0; return { camera: { mode: camera.state.mode, helper: __assign({}, helper.camera.props), stereo: __assign({}, p.camera.stereo), fov: Math.round(radToDeg(camera.state.fov)), manualReset: !!p.camera.manualReset }, cameraFog: camera.state.fog > 0 ? { name: 'on', params: { intensity: camera.state.fog } } : { name: 'off', params: {} }, cameraClipping: { far: camera.state.clipFar, radius: radius, minNear: camera.state.minNear }, cameraResetDurationMs: p.cameraResetDurationMs, sceneRadiusFactor: p.sceneRadiusFactor, transparentBackground: p.transparentBackground, dpoitIterations: p.dpoitIterations, viewport: p.viewport, postprocessing: __assign({}, p.postprocessing), marking: __assign({}, p.marking), multiSample: __assign({}, p.multiSample), renderer: __assign({}, renderer.props), trackball: __assign({}, controls.props), interaction: __assign({}, interactionHelper.props), debug: __assign({}, helper.debug.props), handle: __assign({}, helper.handle.props), }; } var contextRestoredSub = contextRestored.subscribe(function () { pickHelper.dirty = true; draw({ force: true }); // Unclear why, but in Chrome with wboit enabled the first `draw` only clears // the drawingBuffer. Note that in Firefox the drawingBuffer is preserved after // context loss so it is unclear if it behaves the same. draw({ force: true }); }); var resized = new BehaviorSubject(0); function handleResize(draw) { if (draw === void 0) { draw = true; } passes.updateSize(); updateViewport(); syncViewport(); if (draw) requestDraw(); resized.next(+new Date()); } return { webgl: webgl, add: add, remove: remove, commit: commit, update: function (repr, keepSphere) { if (repr) { if (!reprRenderObjects.has(repr)) return; scene.update(repr.renderObjects, !!keepSphere); } else { scene.update(void 0, !!keepSphere); } forceDrawAfterAllCommited = true; }, clear: function () { reprUpdatedSubscriptions.forEach(function (v) { return v.unsubscribe(); }); reprUpdatedSubscriptions.clear(); reprRenderObjects.clear(); scene.clear(); helper.debug.clear(); requestDraw(); reprCount.next(reprRenderObjects.size); }, syncVisibility: function () { if (camera.state.radiusMax === 0) { cameraResetRequested = true; nextCameraResetDuration = 0; } if (scene.syncVisibility()) { if (helper.debug.isEnabled) helper.debug.update(); } requestDraw(); }, requestDraw: requestDraw, tick: tick, animate: animate, resetTime: resetTime, pause: pause, resume: function () { drawPaused = false; }, identify: identify, mark: mark, getLoci: getLoci, handleResize: handleResize, requestResize: function () { resizeRequested = true; }, requestCameraReset: function (options) { nextCameraResetDuration = options === null || options === void 0 ? void 0 : options.durationMs; nextCameraResetSnapshot = options === null || options === void 0 ? void 0 : options.snapshot; cameraResetRequested = true; }, camera: camera, boundingSphere: scene.boundingSphere, boundingSphereVisible: scene.boundingSphereVisible, get notifyDidDraw() { return notifyDidDraw; }, set notifyDidDraw(v) { notifyDidDraw = v; }, didDraw: didDraw, commited: commited, reprCount: reprCount, resized: resized, setProps: function (properties, doNotRequestDraw) { var _a, _b, _c, _d; if (doNotRequestDraw === void 0) { doNotRequestDraw = false; } var props = typeof properties === 'function' ? produce(getProps(), properties) : properties; if (props.sceneRadiusFactor !== undefined) { p.sceneRadiusFactor = props.sceneRadiusFactor; camera.setState({ radiusMax: getSceneRadius() }, 0); } var cameraState = Object.create(null); if (props.camera && props.camera.mode !== undefined && props.camera.mode !== camera.state.mode) { cameraState.mode = props.camera.mode; } var oldFov = Math.round(radToDeg(camera.state.fov)); if (props.camera && props.camera.fov !== undefined && props.camera.fov !== oldFov) { cameraState.fov = degToRad(props.camera.fov); } if (props.cameraFog !== undefined && props.cameraFog.params) { var newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0; if (newFog !== camera.state.fog) cameraState.fog = newFog; } if (props.cameraClipping !== undefined) { if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) { cameraState.clipFar = props.cameraClipping.far; } if (props.cameraClipping.minNear !== undefined && props.cameraClipping.minNear !== camera.state.minNear) { cameraState.minNear = props.cameraClipping.minNear; } if (props.cameraClipping.radius !== undefined) { var radius = (getSceneRadius() / 100) * (100 - props.cameraClipping.radius); if (radius > 0 && radius !== cameraState.radius) { // if radius = 0, NaNs happen cameraState.radius = Math.max(radius, 0.01); } } } if (Object.keys(cameraState).length > 0) camera.setState(cameraState); if ((_a = props.camera) === null || _a === void 0 ? void 0 : _a.helper) helper.camera.setProps(props.camera.helper); if (((_b = props.camera) === null || _b === void 0 ? void 0 : _b.manualReset) !== undefined) p.camera.manualReset = props.camera.manualReset; if (((_c = props.camera) === null || _c === void 0 ? void 0 : _c.stereo) !== undefined) { Object.assign(p.camera.stereo, props.camera.stereo); stereoCamera.setProps(p.camera.stereo.params); } if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs; if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground; if (props.dpoitIterations !== undefined) p.dpoitIterations = props.dpoitIterations; if (props.viewport !== undefined) { var doNotUpdate = p.viewport === props.viewport || (p.viewport.name === props.viewport.name && shallowEqual(p.viewport.params, props.viewport.params)); if (!doNotUpdate) { p.viewport = props.viewport; updateViewport(); syncViewport(); } } if ((_d = props.postprocessing) === null || _d === void 0 ? void 0 : _d.background) { Object.assign(p.postprocessing.background, props.postprocessing.background); passes.draw.postprocessing.background.update(camera, p.postprocessing.background, function (changed) { if (changed && !doNotRequestDraw) requestDraw(); }); } if (props.postprocessing) Object.assign(p.postprocessing, props.postprocessing); if (props.marking) Object.assign(p.marking, props.marking); if (props.multiSample) Object.assign(p.multiSample, props.multiSample); if (props.renderer) renderer.setProps(props.renderer); if (props.trackball) controls.setProps(props.trackball); if (props.interaction) interactionHelper.setProps(props.interaction); if (props.debug) helper.debug.setProps(props.debug); if (props.handle) helper.handle.setProps(props.handle); if (cameraState.mode === 'orthographic') { p.camera.stereo.name = 'off'; } if (!doNotRequestDraw) { requestDraw(); } }, getImagePass: function (props) { if (props === void 0) { props = {}; } return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.wboitEnabled, passes.draw.dpoitEnabled, props); }, getRenderObjects: function () { var renderObjects = []; scene.forEach(function (_, ro) { return renderObjects.push(ro); }); return renderObjects; }, get props() { return getProps(); }, get input() { return input; }, get stats() { return renderer.stats; }, get interaction() { return interactionHelper.events; }, dispose: function () { contextRestoredSub.unsubscribe(); markBuffer = []; scene.clear(); helper.debug.clear(); controls.dispose(); renderer.dispose(); interactionHelper.dispose(); } }; function updateViewport() { var oldX = x, oldY = y, oldWidth = width, oldHeight = height; if (p.viewport.name === 'canvas') { x = 0; y = 0; width = gl.drawingBufferWidth; height = gl.drawingBufferHeight; } else if (p.viewport.name === 'static-frame') { x = p.viewport.params.x * webgl.pixelRatio; height = p.viewport.params.height * webgl.pixelRatio; y = gl.drawingBufferHeight - height - p.viewport.params.y * webgl.pixelRatio; width = p.viewport.params.width * webgl.pixelRatio; } else if (p.viewport.name === 'relative-frame') { x = Math.round(p.viewport.params.x * gl.drawingBufferWidth); height = Math.round(p.viewport.params.height * gl.drawingBufferHeight); y = Math.round(gl.drawingBufferHeight - height - p.viewport.params.y * gl.drawingBufferHeight); width = Math.round(p.viewport.params.width * gl.drawingBufferWidth); } if (oldX !== x || oldY !== y || oldWidth !== width || oldHeight !== height) { forceNextRender = true; } } function syncViewport() { pickHelper.setViewport(x, y, width, height); renderer.setViewport(x, y, width, height); Viewport.set(camera.viewport, x, y, width, height); Viewport.set(controls.viewport, x, y, width, height); } } Canvas3D.create = create; })(Canvas3D || (Canvas3D = {}));