Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(react-components): add useCameraNavigation hook #3581

Merged
merged 4 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions react-components/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
rules: {
'react/react-in-jsx-scope': 'off',
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/no-misused-promises': 'off',
eqeqeq: ['error', 'always']
},
settings: {
Expand Down
2 changes: 1 addition & 1 deletion react-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cognite/reveal-react-components",
"version": "0.9.0",
"version": "0.10.0",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
TreeIndexNodeCollection,
NodeIdNodeCollection,
DefaultNodeAppearance,
type NodeCollection
type NodeCollection,
type Cognite3DViewer
} from '@cognite/reveal';
import { useReveal } from '../RevealContainer/RevealContext';
import { Matrix4 } from 'three';
Expand Down Expand Up @@ -61,16 +62,16 @@ export function CadModelContainer({
}, [modelId, revisionId, geometryFilter]);

useEffect(() => {
if (model === undefined || transform === undefined) return;
if (!modelExists(model, viewer) || transform === undefined) return;
model.setModelTransformation(transform);
}, [transform, model]);

useEffect(() => {
if (model === undefined || styleGroups === undefined) return;
if (!modelExists(model, viewer) || styleGroups === undefined) return;
const stylingCollections = applyStyling(sdk, model, styleGroups);

return () => {
if (model === undefined) return;
if (!modelExists(model, viewer)) return;
void stylingCollections.then((nodeCollections) => {
nodeCollections.forEach((nodeCollection) => {
model.unassignStyledNodeCollection(nodeCollection);
Expand All @@ -80,12 +81,13 @@ export function CadModelContainer({
}, [styleGroups, model]);

useEffect(() => {
if (model === undefined) return;
if (!modelExists(model, viewer)) return;
model.setDefaultNodeAppearance(defaultStyle);
return () => {
if (model !== undefined) {
model.setDefaultNodeAppearance(DefaultNodeAppearance.Default);
if (!modelExists(model, viewer)) {
return;
}
model.setDefaultNodeAppearance(DefaultNodeAppearance.Default);
};
}, [defaultStyle, model]);

Expand Down Expand Up @@ -123,7 +125,7 @@ export function CadModelContainer({
}

function removeModel(): void {
if (model === undefined || !viewer.models.includes(model)) return;
if (!modelExists(model, viewer)) return;

if (cachedViewerRef !== undefined && !cachedViewerRef.isRevealContainerMountedRef.current)
return;
Expand Down Expand Up @@ -153,3 +155,10 @@ async function applyStyling(
}
return collections;
}

function modelExists(
model: CogniteCadModel | undefined,
viewer: Cognite3DViewer
): model is CogniteCadModel {
return model !== undefined && viewer.models.includes(model);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function PointCloudContainer({
}

function cleanStyling(): void {
if (model === undefined) return;
if (model === undefined || !viewer.models.includes(model)) return;

model.setDefaultPointCloudAppearance(DefaultPointCloudAppearance);
model.removeAllStyledObjectCollections();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/*!
* Copyright 2023 Cognite AS
*/
import { useRef, type ReactElement, useContext, useState, useEffect } from 'react';
import { useRef, type ReactElement, useState, useEffect } from 'react';
import { type Cognite3DViewer, type PointerEventData } from '@cognite/reveal';
import { ModelsLoadingStateContext } from './ModelsLoadingContext';
import { CadModelContainer, type CadModelStyling } from '../CadModelContainer/CadModelContainer';
import {
PointCloudContainer,
Expand All @@ -27,11 +26,11 @@ export const Reveal3DResources = ({
resources,
defaultResourceStyling,
instanceStyling,
onNodeClick
onNodeClick,
onResourcesAdded
}: Reveal3DResourcesProps): ReactElement => {
const [reveal3DModels, setReveal3DModels] = useState<TypedReveal3DModel[]>([]);

const { setModelsAdded } = useContext(ModelsLoadingStateContext);
const viewer = useReveal();
const fdmSdk = useFdmSdk();
const client = useSDK();
Expand Down Expand Up @@ -73,8 +72,8 @@ export const Reveal3DResources = ({
const onModelLoaded = (): void => {
numModelsLoaded.current += 1;

if (numModelsLoaded.current === resources.length) {
setModelsAdded(true);
if (numModelsLoaded.current === resources.length && onResourcesAdded !== undefined) {
onResourcesAdded();
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ export type Reveal3DResourcesProps = {
defaultResourceStyling?: DefaultResourceStyling;
instanceStyling?: FdmAssetStylingGroup[];
onNodeClick?: (node: Promise<NodeDataResult | undefined>) => void;
onResourcesAdded?: () => void;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { createPortal } from 'react-dom';
import { Cognite3DViewer, type Cognite3DViewerOptions } from '@cognite/reveal';
import { RevealContext } from './RevealContext';
import { type Color } from 'three';
import { ModelsLoadingStateContext } from '../Reveal3DResources/ModelsLoadingContext';
import { SDKProvider } from './SDKProvider';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { useRevealKeepAlive } from '../RevealKeepAlive/RevealKeepAliveContext';
Expand Down Expand Up @@ -78,9 +77,7 @@ export function RevealContainer({
<>
<RevealContainerElementContext.Provider value={wrapperDomElement.current}>
<RevealContext.Provider value={viewer}>
<ModelsLoadingProvider>
{createPortal(children, viewerDomElement.current)}
</ModelsLoadingProvider>
{createPortal(children, viewerDomElement.current)}
</RevealContext.Provider>
</RevealContainerElementContext.Provider>
</>
Expand All @@ -104,13 +101,3 @@ export function RevealContainer({
return viewer;
}
}

function ModelsLoadingProvider({ children }: { children?: ReactNode }): ReactElement {
const [modelsLoading, setModelsLoading] = useState(false);
return (
<ModelsLoadingStateContext.Provider
value={{ modelsAdded: modelsLoading, setModelsAdded: setModelsLoading }}>
{children}
</ModelsLoadingStateContext.Provider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@

import { type ReactElement, useCallback } from 'react';

import { Box3 } from 'three';

import { useReveal } from '../RevealContainer/RevealContext';
import { Button } from '@cognite/cogs.js';
import { useCameraNavigation } from '../../hooks/useCameraNavigation';

export const FitModelsButton = (): ReactElement => {
const viewer = useReveal();
const cameraNavigation = useCameraNavigation();

const updateCamera = useCallback(() => {
const box = new Box3();

viewer.models.forEach((model) => box.union(model.getModelBoundingBox()));

viewer.cameraManager.fitCameraToBoundingBox(box);
}, [viewer, ...viewer.models]);
cameraNavigation.fitCameraToAllModels();
}, []);

return (
<Button
Expand Down
68 changes: 68 additions & 0 deletions react-components/src/hooks/useCameraNavigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*!
* Copyright 2023 Cognite AS
*/

import { type CogniteCadModel } from '@cognite/reveal';
import { useReveal } from '../components/RevealContainer/RevealContext';
import { useFdmSdk } from '../components/RevealContainer/SDKProvider';
import { SYSTEM_3D_EDGE_SOURCE, type InModel3dEdgeProperties } from '../utilities/globalDataModels';

export type CameraNavigationActions = {
fitCameraToAllModels: () => void;
fitCameraToModelNode: (revisionId: number, nodeId: number) => Promise<void>;
fitCameraToInstance: (externalId: string, space: string) => Promise<void>;
};

export const useCameraNavigation = (): CameraNavigationActions => {
const viewer = useReveal();
const fdmSDK = useFdmSdk();

const fitCameraToAllModels = (): void => {
const models = viewer.models;
if (models.length === 0) {
return;
}
viewer.fitCameraToModels(models, undefined, true);
};

const fitCameraToModelNode = async (revisionId: number, nodeId: number): Promise<void> => {
const model = viewer.models.find((m) => m.revisionId === revisionId);
christjt marked this conversation as resolved.
Show resolved Hide resolved
if (model === undefined) {
await Promise.reject(new Error(`Could not find model with revision ${revisionId}`));
return;
}
const nodeBoundingBox = await (model as CogniteCadModel).getBoundingBoxByNodeId(nodeId);
viewer.cameraManager.fitCameraToBoundingBox(nodeBoundingBox);
};

const fitCameraToInstance = async (externalId: string, space: string): Promise<void> => {
const fdmAssetMappingFilter = {
equals: {
property: ['edge', 'startNode'],
value: { externalId, space }
}
};

const assetEdges = await fdmSDK.filterInstances<InModel3dEdgeProperties>(
fdmAssetMappingFilter,
'edge',
SYSTEM_3D_EDGE_SOURCE
);

if (assetEdges.edges.length === 0) {
await Promise.reject(
new Error(`Could not find a connected model to instance ${externalId} in space ${space}`)
);
return;
}

const { revisionId, revisionNodeId } = assetEdges.edges[0].properties;
await fitCameraToModelNode(revisionId, revisionNodeId);
};

return {
fitCameraToAllModels,
fitCameraToInstance,
fitCameraToModelNode
};
};
3 changes: 2 additions & 1 deletion react-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ export { CadModelContainer } from './components/CadModelContainer/CadModelContai
export { Image360CollectionContainer } from './components/Image360CollectionContainer/Image360CollectionContainer';
export { Image360HistoricalDetails } from './components/Image360HistoricalDetails/Image360HistoricalDetails';
export { ViewerAnchor } from './components/ViewerAnchor/ViewerAnchor';
export { CameraController } from './components/CameraController/CameraController';
export { RevealToolbar } from './components/RevealToolbar/RevealToolbar';
export { RevealKeepAlive } from './components/RevealKeepAlive/RevealKeepAlive';

// Hooks
export { useReveal } from './components/RevealContainer/RevealContext';
export { use3DModelName } from './hooks/use3DModelName';
export { useFdmAssetMappings } from './hooks/useFdmAssetMappings';
export { useCameraNavigation } from './hooks/useCameraNavigation';

// Higher order components
export { withSuppressRevealEvents } from './higher-order-components/withSuppressRevealEvents';
Expand All @@ -44,4 +44,5 @@ export type {
AddReveal3DModelOptions,
NodeDataResult
} from './components/Reveal3DResources/types';
export type { CameraNavigationActions } from './hooks/useCameraNavigation';
export type { Source } from './utilities/FdmSDK';
10 changes: 2 additions & 8 deletions react-components/stories/CadModelContainer.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* Copyright 2023 Cognite AS
*/
import type { Meta, StoryObj } from '@storybook/react';
import { CadModelContainer, CameraController, RevealContainer } from '../src';
import { Color, Matrix4, Vector3 } from 'three';
import { CadModelContainer, RevealContainer } from '../src';
import { Color, Matrix4 } from 'three';
import { createSdkByUrlToken } from './utilities/createSdkByUrlToken';
import { NumericRange } from '@cognite/reveal';

Expand Down Expand Up @@ -84,12 +84,6 @@ export const Main: Story = {
<RevealContainer sdk={sdk} color={new Color(0x4a4a4a)}>
<CadModelContainer addModelOptions={addModelOptions} styling={styling} />
<CadModelContainer addModelOptions={addModelOptions} transform={transform} />
<CameraController
initialFitCamera={{
to: 'cameraState',
state: { position: new Vector3(10, 20, 15), target: new Vector3(10, 0, -10) }
}}
/>
</RevealContainer>
)
};
Loading
Loading