import { PostProcessStageComposite as CesiumPostProcessStageComposite } from "cesium"; import type { PickCesiumProps, ConstructorOptions, Merge } from "../core"; import { createCesiumComponent } from "../core"; /* @summary `PostProcessStagComposite` can add a post processing stages to the scene. Bult-in PostProcessStageComposite components are available with additional Cesium properties: | Component | Property | Type | |---|---|---| | AmbientOcclusion | intensity | number | | | bias | number | | | lengthCap | number | | | stepSize | number | | | frustumLength | number | | | ambientOcclusionOnly | boolean | | | delta | number | | | sigma | number | | Bloom | contrast | number | | | brightness | number | | | glowOnly | boolean | | | delta | number | | | sigma | number | | | stepSize | number | | DepthOfFieldStage | focalDistance | number | | | delta | number | | | sigma | number | | | stepSize | number | | EdgeDetectionStage | color | [Color](https://cesium.com/docs/cesiumjs-ref-doc/Color.html) | | | length | number || | NightVisionStage | - | - | | SilhouetteStage | color | [Color](https://cesium.com/docs/cesiumjs-ref-doc/Color.html) | | | length | number | Note: `AmbientOcclusion` and `Bloom` components can not be used multi time for each components, as it refers to the single post process stage of the scene. They can be imported and used normally: ```jsx import { AmbientOcclusion, Bloom } from "resium"; ``` */ /* @scope Inside [Viewer](/components/Viewer) or [CesiumWidget](/components/CesiumWidget) components. */ export type Target = Merge< CesiumPostProcessStageComposite, ConstructorOptions >; export type PostProcessStageCompositeCesiumProps = PickCesiumProps; export type PostProcessStageCompositeCesiumReadonlyProps = PickCesiumProps< Target, typeof cesiumReadonlyProps, "stages" >; export type PostProcessStageCompositeProps = PostProcessStageCompositeCesiumProps & PostProcessStageCompositeCesiumReadonlyProps; const cesiumProps = ["enabled", "selected"] as const; const cesiumReadonlyProps = ["inputPreviousStageTexture", "name", "stages", "uniforms"] as const; type CompositeInternals = { _stages?: unknown[] }; type StageInternals = { name?: string; _textureCache?: unknown; _index?: unknown }; type CollectionInternals = { remove(stage: CesiumPostProcessStageComposite): boolean; _stageNames?: Record; }; /** * Removes a resium-owned `PostProcessStageComposite` from the scene's * `PostProcessStageCollection` and destroys it WITHOUT destroying the * user-supplied child `PostProcessStage` instances passed via the `stages` prop. * * resium creates and owns the composite, but it does NOT own the child stages. * Both `PostProcessStageCollection.remove(stage)` and * `PostProcessStageComposite.destroy()` unconditionally destroy every child * stage (Cesium offers no `destroy=false` flag). Under React StrictMode * (mount -> unmount -> remount), the throwaway first unmount would destroy the * user-owned stages, so the real remount reuses already-destroyed stages and * throws "This object was destroyed, i.e., destroy() was called." (same class * of bug as #602). * * The composite stores the user's stages in its internal `_stages` array, which * is the SAME array reference passed in props (`this._stages = options.stages`). * When the collection adds the composite, `add` walks into the children and * registers each one in the collection's `_stageNames` map and stamps * `child._textureCache`. We: * 1. snapshot the children and replace the composite's `_stages` with a fresh * empty array (NOT emptying the user's array in place) so neither `remove` * nor `destroy` cascades into the children; * 2. `remove` the (now child-less) composite, which frees the composite's own * name and slot; * 3. reverse the per-child registration that `add` performed (delete the name * entry and clear `_textureCache`/`_index`), leaving the user-owned stages * intact and re-addable on the next mount. * * @internal exported for testing. */ export const removeCompositeWithoutDestroyingChildren = ( element: CesiumPostProcessStageComposite, collection: CollectionInternals | undefined, ): void => { const internal = element as unknown as CompositeInternals; const children = Array.isArray(internal._stages) ? internal._stages.slice() : []; // Replace (do not mutate) the array: `_stages` aliases the user's `stages` // prop array, so emptying it in place would corrupt the caller's array. internal._stages = []; if (collection) { collection.remove(element); const stageNames = collection._stageNames; for (const child of children) { const c = child as StageInternals; if (stageNames && typeof c.name === "string") { Reflect.deleteProperty(stageNames, c.name); } c._textureCache = undefined; c._index = undefined; } } if (!element.isDestroyed()) { element.destroy(); } }; export const PostProcessStageComposite = createCesiumComponent< CesiumPostProcessStageComposite, PostProcessStageCompositeProps >({ name: "PostProcessStageComposite", create(context, props) { if (!context.scene) return; const element = new CesiumPostProcessStageComposite(props); if (typeof props.enabled === "boolean") { element.enabled = props.enabled; } if (props.selected) { element.selected = props.selected; } context.scene.postProcessStages.add(element); return element; }, destroy(element, context) { const collection = context.scene && !context.scene.isDestroyed() ? (context.scene.postProcessStages as unknown as CollectionInternals) : undefined; removeCompositeWithoutDestroyingChildren(element, collection); }, cesiumProps, cesiumReadonlyProps, }); export default PostProcessStageComposite;