From ba0494b8d99036d27d8af250196b6a65f7462f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 7 Aug 2023 13:11:47 +0200 Subject: [PATCH 01/23] feat: useClickedNode hook --- .../higher-order-components/ClickedNode.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 react-components/src/higher-order-components/ClickedNode.tsx diff --git a/react-components/src/higher-order-components/ClickedNode.tsx b/react-components/src/higher-order-components/ClickedNode.tsx new file mode 100644 index 00000000000..3a15aafa3b9 --- /dev/null +++ b/react-components/src/higher-order-components/ClickedNode.tsx @@ -0,0 +1,48 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { PointerEventData } from '@cognite/reveal'; +import { ReactElement, createContext, useContext, useEffect, useState } from 'react'; +import { FdmAssetMappingsConfig, NodeDataResult, useReveal } from '..'; +import { queryMappedData } from '../components/Reveal3DResources/queryMappedData'; +import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; + + +type ClickedNodeProps = { + children?: ReactElement; + fdmConfig?: FdmAssetMappingsConfig; +}; + +const ClickedNodeContext = createContext(undefined); + +export const ClickedNode = ({ fdmConfig, children }: ClickedNodeProps) => { + const viewer = useReveal(); + const sdk = useSDK(); + const fdmClient = useFdmSdk(); + + const [clickedNode, setClickedNode] = useState(null); + + useEffect(() => { + + const callback = async (pointerEvent: PointerEventData) => { + const nodeData = await queryMappedData(viewer, sdk, fdmClient, pointerEvent, fdmConfig); + setClickedNode(nodeData ?? null); + }; + + viewer.on('click', (event: PointerEventData) => void callback(event)); + }, [viewer]); + + + return + {children} + +} + +export const useClickedNode = (): NodeDataResult | null => { + const nodeData = useContext(ClickedNodeContext); + if (nodeData === undefined) { + throw Error('useClickedNode must be used within a ClickedNode context'); + } + return nodeData; +} From 578a99cb1af8a031edbe27fb138ab081c26a19f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 7 Aug 2023 16:27:28 +0200 Subject: [PATCH 02/23] feat(WIP): ClickedNode context --- .../higher-order-components/ClickedNode.tsx | 4 +- react-components/src/index.ts | 1 + .../stories/ClickedNode.stories.tsx | 70 +++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 react-components/stories/ClickedNode.stories.tsx diff --git a/react-components/src/higher-order-components/ClickedNode.tsx b/react-components/src/higher-order-components/ClickedNode.tsx index 3a15aafa3b9..3d44c5bf0cd 100644 --- a/react-components/src/higher-order-components/ClickedNode.tsx +++ b/react-components/src/higher-order-components/ClickedNode.tsx @@ -3,14 +3,14 @@ */ import { PointerEventData } from '@cognite/reveal'; -import { ReactElement, createContext, useContext, useEffect, useState } from 'react'; +import { ReactNode, createContext, useContext, useEffect, useState } from 'react'; import { FdmAssetMappingsConfig, NodeDataResult, useReveal } from '..'; import { queryMappedData } from '../components/Reveal3DResources/queryMappedData'; import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; type ClickedNodeProps = { - children?: ReactElement; + children?: ReactNode; fdmConfig?: FdmAssetMappingsConfig; }; diff --git a/react-components/src/index.ts b/react-components/src/index.ts index a897db6522d..40b21857121 100644 --- a/react-components/src/index.ts +++ b/react-components/src/index.ts @@ -21,6 +21,7 @@ export { useFdmAssetMappings } from './hooks/useFdmAssetMappings'; // Higher order components export { withSuppressRevealEvents } from './higher-order-components/withSuppressRevealEvents'; +export { useClickedNode } from './higher-order-components/ClickedNode'; // Types export { diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx new file mode 100644 index 00000000000..37050f89188 --- /dev/null +++ b/react-components/stories/ClickedNode.stories.tsx @@ -0,0 +1,70 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { + Reveal3DResources, + RevealContainer, + RevealToolbar, + useClickedNode, +} from '../src'; +import { CogniteClient } from '@cognite/sdk'; +import { Color } from 'three'; +import { ClickedNode } from '../src/higher-order-components/ClickedNode'; +import { useEffect } from 'react'; + +import { DefaultFdmConfig } from './utilities/fdmConfig'; + +const meta = { + title: 'Example/ClickedNode', + component: Reveal3DResources, + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + + +const token = new URLSearchParams(window.location.search).get('token') ?? ''; +const sdk = new CogniteClient({ + appId: 'reveal.example', + baseUrl: 'https://greenfield.cognitedata.com', + project: '3d-test', + getToken: async () => await Promise.resolve(token) +}); + +export const Main: Story = { + args: { + resources: [ + { + modelId: 2551525377383868, + revisionId: 2143672450453400 + } + ], + styling: {}, + fdmAssetMappingConfig: DefaultFdmConfig + }, + render: ({ resources, fdmAssetMappingConfig }) => { + return ( + + + + + + + + + + ); + } +}; + +const ClickedNodePrinter = () => { + const clickedNode = useClickedNode(); + useEffect(() => { + console.log('Clicked node is ', clickedNode); + }, [clickedNode]); + + return <>; +} From 16a1f069472550227e8c1af3ac759aa1485ef39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 7 Aug 2023 16:36:23 +0200 Subject: [PATCH 03/23] chore: lint fix --- .../higher-order-components/ClickedNode.tsx | 32 +++++++++++-------- .../stories/ClickedNode.stories.tsx | 22 ++++--------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/react-components/src/higher-order-components/ClickedNode.tsx b/react-components/src/higher-order-components/ClickedNode.tsx index 3d44c5bf0cd..904da15b28e 100644 --- a/react-components/src/higher-order-components/ClickedNode.tsx +++ b/react-components/src/higher-order-components/ClickedNode.tsx @@ -2,13 +2,19 @@ * Copyright 2023 Cognite AS */ -import { PointerEventData } from '@cognite/reveal'; -import { ReactNode, createContext, useContext, useEffect, useState } from 'react'; -import { FdmAssetMappingsConfig, NodeDataResult, useReveal } from '..'; +import { type PointerEventData } from '@cognite/reveal'; +import { + type ReactNode, + createContext, + useContext, + useEffect, + useState, + type ReactElement +} from 'react'; +import { type FdmAssetMappingsConfig, type NodeDataResult, useReveal } from '..'; import { queryMappedData } from '../components/Reveal3DResources/queryMappedData'; import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; - type ClickedNodeProps = { children?: ReactNode; fdmConfig?: FdmAssetMappingsConfig; @@ -16,7 +22,7 @@ type ClickedNodeProps = { const ClickedNodeContext = createContext(undefined); -export const ClickedNode = ({ fdmConfig, children }: ClickedNodeProps) => { +export const ClickedNode = ({ fdmConfig, children }: ClickedNodeProps): ReactElement => { const viewer = useReveal(); const sdk = useSDK(); const fdmClient = useFdmSdk(); @@ -24,20 +30,18 @@ export const ClickedNode = ({ fdmConfig, children }: ClickedNodeProps) => { const [clickedNode, setClickedNode] = useState(null); useEffect(() => { - - const callback = async (pointerEvent: PointerEventData) => { + const callback = async (pointerEvent: PointerEventData): Promise => { const nodeData = await queryMappedData(viewer, sdk, fdmClient, pointerEvent, fdmConfig); setClickedNode(nodeData ?? null); }; - viewer.on('click', (event: PointerEventData) => void callback(event)); + viewer.on('click', (event: PointerEventData) => { + void callback(event); + }); }, [viewer]); - - return - {children} - -} + return {children}; +}; export const useClickedNode = (): NodeDataResult | null => { const nodeData = useContext(ClickedNodeContext); @@ -45,4 +49,4 @@ export const useClickedNode = (): NodeDataResult | null => { throw Error('useClickedNode must be used within a ClickedNode context'); } return nodeData; -} +}; diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx index 37050f89188..1664f89a1e4 100644 --- a/react-components/stories/ClickedNode.stories.tsx +++ b/react-components/stories/ClickedNode.stories.tsx @@ -3,16 +3,11 @@ */ import type { Meta, StoryObj } from '@storybook/react'; -import { - Reveal3DResources, - RevealContainer, - RevealToolbar, - useClickedNode, -} from '../src'; +import { Reveal3DResources, RevealContainer, RevealToolbar, useClickedNode } from '../src'; import { CogniteClient } from '@cognite/sdk'; import { Color } from 'three'; import { ClickedNode } from '../src/higher-order-components/ClickedNode'; -import { useEffect } from 'react'; +import { type ReactElement, useEffect } from 'react'; import { DefaultFdmConfig } from './utilities/fdmConfig'; @@ -25,7 +20,6 @@ const meta = { export default meta; type Story = StoryObj; - const token = new URLSearchParams(window.location.search).get('token') ?? ''; const sdk = new CogniteClient({ appId: 'reveal.example', @@ -48,23 +42,21 @@ export const Main: Story = { render: ({ resources, fdmAssetMappingConfig }) => { return ( - + - - - - + + ); } }; -const ClickedNodePrinter = () => { +const ClickedNodePrinter = (): ReactElement => { const clickedNode = useClickedNode(); useEffect(() => { console.log('Clicked node is ', clickedNode); }, [clickedNode]); return <>; -} +}; From 9583185fbb04bc6405885f1d6a88f5a92c0f8119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 8 Aug 2023 11:23:45 +0200 Subject: [PATCH 04/23] refactor: make data query function into a hook --- .../Reveal3DResources/Reveal3DResources.tsx | 22 +- .../Reveal3DResources/queryMappedData.ts | 151 ------------- .../Reveal3DResources/useNodeMappedData.tsx | 201 ++++++++++++++++++ .../higher-order-components/ClickedNode.tsx | 13 +- 4 files changed, 218 insertions(+), 169 deletions(-) delete mode 100644 react-components/src/components/Reveal3DResources/queryMappedData.ts create mode 100644 react-components/src/components/Reveal3DResources/useNodeMappedData.tsx diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 6ce675b979e..5deb59fbb9a 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -26,7 +26,7 @@ import { import { type CogniteExternalId } from '@cognite/sdk'; import { type FdmAssetMappingsConfig } from '../../hooks/types'; import { useCalculateModelsStyling } from '../../hooks/useCalculateModelsStyling'; -import { queryMappedData } from './queryMappedData'; +import { useNodeMappedData } from './useNodeMappedData'; import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; export type FdmAssetStylingGroup = { @@ -59,28 +59,30 @@ export const Reveal3DResources = ({ const { setModelsAdded } = useContext(ModelsLoadingStateContext); const viewer = useReveal(); - const fdmSdk = useFdmSdk(); - const client = useSDK(); const numModelsLoaded = useRef(0); + const [lastClickEvent, setLastClickEvent] = useState(undefined); + useEffect(() => { getTypedModels(resources, viewer).then(setReveal3DModels).catch(console.error); }, [resources, viewer]); const modelsStyling = useCalculateModelsStyling(reveal3DModels, styling, fdmAssetMappingConfig); + const clickedNodeData = useNodeMappedData(lastClickEvent, fdmAssetMappingConfig); useEffect(() => { setReveal3DModelsStyling(modelsStyling); }, [modelsStyling]); useEffect(() => { - const callback = (event: PointerEventData): void => { - void (async (event: PointerEventData): Promise => { - if (onNodeClick === undefined) return; - const data = await queryMappedData(viewer, client, fdmSdk, event, fdmAssetMappingConfig); + if (clickedNodeData !== undefined) { + onNodeClick?.(clickedNodeData); + } + }, [lastClickEvent, clickedNodeData]); - onNodeClick(data); - })(event); + useEffect(() => { + const callback = (event: PointerEventData): void => { + setLastClickEvent(event); }; viewer.on('click', callback); @@ -88,7 +90,7 @@ export const Reveal3DResources = ({ return () => { viewer.off('click', callback); }; - }, [viewer, client, fdmSdk, fdmAssetMappingConfig, onNodeClick]); + }, [viewer, onNodeClick]); const image360CollectionAddOptions = resources.filter( (resource): resource is AddImageCollection360Options => diff --git a/react-components/src/components/Reveal3DResources/queryMappedData.ts b/react-components/src/components/Reveal3DResources/queryMappedData.ts deleted file mode 100644 index 31ecd8b7d69..00000000000 --- a/react-components/src/components/Reveal3DResources/queryMappedData.ts +++ /dev/null @@ -1,151 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ - -import { type Cognite3DViewer, type PointerEventData, type CogniteCadModel } from '@cognite/reveal'; -import { type CogniteInternalId, type CogniteClient, type Node3D } from '@cognite/sdk'; -import { - type EdgeItem, - type InspectResultList, - type FdmSDK, - type DmsUniqueIdentifier -} from '../../utilities/FdmSDK'; -import { type FdmAssetMappingsConfig } from '../../hooks/types'; -import { type NodeDataResult } from './types'; -import assert from 'assert'; - -export async function queryMappedData( - viewer: Cognite3DViewer, - cdfClient: CogniteClient, - fdmClient: FdmSDK, - clickEvent: PointerEventData, - fdmConfig?: FdmAssetMappingsConfig -): Promise { - if (fdmConfig === undefined) { - throw Error('Must supply fdmConfig when using FDM queries'); - } - - const intersection = await viewer.getIntersectionFromPixel( - clickEvent.offsetX, - clickEvent.offsetY - ); - - if (intersection === null || intersection.type !== 'cad') { - return; - } - - const cadIntersection = intersection; - const model = cadIntersection.model; - - const ancestors = await getAncestorNodesForTreeIndex(cdfClient, model, cadIntersection.treeIndex); - - const mappings = await getMappingEdges( - fdmClient, - fdmConfig, - model, - ancestors.map((n) => n.id) - ); - - if (mappings.edges.length === 0) { - return; - } - - const selectedEdge = mappings.edges[0]; - const selectedNodeId = - selectedEdge.properties[fdmConfig.source.space][ - `${fdmConfig.source.externalId}/${fdmConfig.source.version}` - ].revisionNodeId; - const selectedNode = ancestors.find((n) => n.id === selectedNodeId); - assert(selectedNode !== undefined); - - const dataNode = selectedEdge.startNode; - - const inspectionResult = await inspectNode(fdmClient, dataNode); - - const dataView = - inspectionResult.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; - - return { - nodeExternalId: dataNode.externalId, - view: dataView, - cadNode: selectedNode, - intersection: cadIntersection - }; -} - -async function getAncestorNodesForTreeIndex( - client: CogniteClient, - model: CogniteCadModel, - treeIndex: number -): Promise { - const nodeId = await model.mapTreeIndexToNodeId(treeIndex); - - const ancestorNodes = await client.revisions3D.list3DNodeAncestors( - model.modelId, - model.revisionId, - nodeId - ); - - return ancestorNodes.items; -} - -async function getMappingEdges( - fdmClient: FdmSDK, - fdmConfig: FdmAssetMappingsConfig, - model: CogniteCadModel, - ancestorIds: CogniteInternalId[] -): Promise<{ edges: Array>> }> { - const filter = { - and: [ - { - equals: { - property: ['edge', 'endNode'], - value: { - space: fdmConfig.global3dSpace, - externalId: `model_3d_${model.modelId}` - } - } - }, - { - equals: { - property: [ - fdmConfig.source.space, - `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, - 'revisionId' - ], - value: model.revisionId - } - }, - { - in: { - property: [ - fdmConfig.source.space, - `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, - 'revisionNodeId' - ], - values: ancestorIds - } - } - ] - }; - - return await fdmClient.filterAllInstances(filter, 'edge', fdmConfig.source); -} - -async function inspectNode( - fdmClient: FdmSDK, - dataNode: DmsUniqueIdentifier -): Promise { - const inspectionResult = await fdmClient.inspectInstances({ - inspectionOperations: { involvedViewsAndContainers: {} }, - items: [ - { - instanceType: 'node', - externalId: dataNode.externalId, - space: dataNode.space - } - ] - }); - - return inspectionResult; -} diff --git a/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx b/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx new file mode 100644 index 00000000000..5a3c30336d6 --- /dev/null +++ b/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx @@ -0,0 +1,201 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { useQuery } from '@tanstack/react-query'; + +import { type PointerEventData, type CogniteCadModel, type CadIntersection } from '@cognite/reveal'; +import { type CogniteInternalId, type Node3D } from '@cognite/sdk'; +import { + type EdgeItem, + type InspectResultList, + type DmsUniqueIdentifier +} from '../../utilities/FdmSDK'; +import { type FdmAssetMappingsConfig } from '../../hooks/types'; +import { type NodeDataResult } from './types'; +import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; +import { useEffect, useState } from 'react'; +import { useReveal } from '../..'; + +import assert from 'assert'; + +export const useNodeMappedData = ( + clickEvent: PointerEventData | undefined, + fdmConfig?: FdmAssetMappingsConfig +): NodeDataResult | undefined => { + + const viewer = useReveal(); + + const [cadIntersection, setCadIntersection] = useState(undefined); + + useEffect(() => { + (async () => { + + if (clickEvent === undefined) { + return; + } + + const intersection = await viewer.getIntersectionFromPixel( + clickEvent.offsetX, + clickEvent.offsetY + ); + + if (intersection === null || intersection.type !== 'cad') { + return; + } + + const cadIntersection = intersection; + setCadIntersection(cadIntersection); + })(); + }, [clickEvent]); + + const ancestors = useAncestorNodesForTreeIndex(cadIntersection?.model, cadIntersection?.treeIndex); + + const mappings = useNodeMappingEdges( + fdmConfig, + cadIntersection?.model, + ancestors?.map(n => n.id) + ); + + const selectedEdge = mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; + const selectedNodeId = + fdmConfig === undefined ? undefined : + selectedEdge?.properties[fdmConfig.source.space][ + `${fdmConfig?.source.externalId}/${fdmConfig.source.version}` + ].revisionNodeId; + + const dataNode = selectedEdge?.startNode; + + const inspectionResult = useInspectNode(dataNode); + + const dataView = + inspectionResult?.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; + + const selectedNode = ancestors?.find((n) => n.id === selectedNodeId); + + if (selectedNode === undefined || + dataView === undefined || + dataNode === undefined || + cadIntersection === undefined) { + return undefined; + } + + return { + nodeExternalId: dataNode.externalId, + view: dataView, + cadNode: selectedNode, + intersection: cadIntersection + }; +} + +function useAncestorNodesForTreeIndex( + model: CogniteCadModel | undefined, + treeIndex: number | undefined +): Node3D[] | undefined { + + const cogniteClient = useSDK(); + + const queryResult = useQuery( + ['cdf', '3d', 'tree-index-to-ancestors', `${model?.modelId}-${model?.revisionId}-treeIndex`], + async () => { + assert(model !== undefined && treeIndex !== undefined); + + const nodeId = await model.mapTreeIndexToNodeId(treeIndex); + + const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( + model.modelId, + model.revisionId, + nodeId + ); + + return ancestorNodes.items; + }, + { enabled: model !== undefined && treeIndex !== undefined }); + + return queryResult.data; +} + +function useNodeMappingEdges( + fdmConfig: FdmAssetMappingsConfig | undefined, + model: CogniteCadModel | undefined, + ancestorIds: CogniteInternalId[] | undefined +): { edges: Array>> } | undefined { + + const fdmClient = useFdmSdk(); + + const queryResult = useQuery( + ['fdm', '3d', 'node-mapping-edges', ancestorIds], + async () => { + + assert(fdmConfig !== undefined && model !== undefined && ancestorIds !== undefined && ancestorIds.length !== 0); + + const filter = { + and: [ + { + equals: { + property: ['edge', 'endNode'], + value: { + space: fdmConfig.global3dSpace, + externalId: `model_3d_${model.modelId}` + } + } + }, + { + equals: { + property: [ + fdmConfig.source.space, + `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, + 'revisionId' + ], + value: model.revisionId + } + }, + { + in: { + property: [ + fdmConfig.source.space, + `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, + 'revisionNodeId' + ], + values: ancestorIds + } + } + ] + }; + + return fdmClient.filterAllInstances(filter, 'edge', fdmConfig.source); + }, { + enabled: fdmConfig !== undefined && model !== undefined && ancestorIds !== undefined && ancestorIds.length !== 0 + }); + + return queryResult.data; +} + +function useInspectNode( + dataNode: DmsUniqueIdentifier | undefined +): InspectResultList | undefined { + + const fdmClient = useFdmSdk(); + + const inspectionResult = useQuery( + ['fdm', '3d', `inspect-${dataNode?.space}-${dataNode?.space}`], + + () => { + assert(dataNode !== undefined); + + return fdmClient.inspectInstances({ + inspectionOperations: { involvedViewsAndContainers: {} }, + items: [ + { + instanceType: 'node', + externalId: dataNode.externalId, + space: dataNode.space + } + ] + }) + }, { + enabled: dataNode !== undefined + }); + + return inspectionResult.data; +} diff --git a/react-components/src/higher-order-components/ClickedNode.tsx b/react-components/src/higher-order-components/ClickedNode.tsx index 904da15b28e..def69a1bf74 100644 --- a/react-components/src/higher-order-components/ClickedNode.tsx +++ b/react-components/src/higher-order-components/ClickedNode.tsx @@ -12,8 +12,7 @@ import { type ReactElement } from 'react'; import { type FdmAssetMappingsConfig, type NodeDataResult, useReveal } from '..'; -import { queryMappedData } from '../components/Reveal3DResources/queryMappedData'; -import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; +import { useNodeMappedData } from '../components/Reveal3DResources/useNodeMappedData'; type ClickedNodeProps = { children?: ReactNode; @@ -24,15 +23,13 @@ const ClickedNodeContext = createContext(unde export const ClickedNode = ({ fdmConfig, children }: ClickedNodeProps): ReactElement => { const viewer = useReveal(); - const sdk = useSDK(); - const fdmClient = useFdmSdk(); - const [clickedNode, setClickedNode] = useState(null); + const [lastClickEvent, setLastClickEvent] = useState(undefined); + const nodeData = useNodeMappedData(lastClickEvent, fdmConfig); useEffect(() => { const callback = async (pointerEvent: PointerEventData): Promise => { - const nodeData = await queryMappedData(viewer, sdk, fdmClient, pointerEvent, fdmConfig); - setClickedNode(nodeData ?? null); + setLastClickEvent(pointerEvent); }; viewer.on('click', (event: PointerEventData) => { @@ -40,7 +37,7 @@ export const ClickedNode = ({ fdmConfig, children }: ClickedNodeProps): ReactEle }); }, [viewer]); - return {children}; + return {children}; }; export const useClickedNode = (): NodeDataResult | null => { From 67326153c9574aae5c9c2de8566462b63e93a623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 8 Aug 2023 12:17:58 +0200 Subject: [PATCH 05/23] feat: introduce `useClickedNode` hook instead of higher-order component --- .../higher-order-components/ClickedNode.tsx | 49 ------------------- react-components/src/hooks/useClickedNode.tsx | 26 ++++++++++ react-components/src/index.ts | 2 +- .../stories/ClickedNode.stories.tsx | 14 +++--- 4 files changed, 33 insertions(+), 58 deletions(-) delete mode 100644 react-components/src/higher-order-components/ClickedNode.tsx create mode 100644 react-components/src/hooks/useClickedNode.tsx diff --git a/react-components/src/higher-order-components/ClickedNode.tsx b/react-components/src/higher-order-components/ClickedNode.tsx deleted file mode 100644 index def69a1bf74..00000000000 --- a/react-components/src/higher-order-components/ClickedNode.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ - -import { type PointerEventData } from '@cognite/reveal'; -import { - type ReactNode, - createContext, - useContext, - useEffect, - useState, - type ReactElement -} from 'react'; -import { type FdmAssetMappingsConfig, type NodeDataResult, useReveal } from '..'; -import { useNodeMappedData } from '../components/Reveal3DResources/useNodeMappedData'; - -type ClickedNodeProps = { - children?: ReactNode; - fdmConfig?: FdmAssetMappingsConfig; -}; - -const ClickedNodeContext = createContext(undefined); - -export const ClickedNode = ({ fdmConfig, children }: ClickedNodeProps): ReactElement => { - const viewer = useReveal(); - - const [lastClickEvent, setLastClickEvent] = useState(undefined); - const nodeData = useNodeMappedData(lastClickEvent, fdmConfig); - - useEffect(() => { - const callback = async (pointerEvent: PointerEventData): Promise => { - setLastClickEvent(pointerEvent); - }; - - viewer.on('click', (event: PointerEventData) => { - void callback(event); - }); - }, [viewer]); - - return {children}; -}; - -export const useClickedNode = (): NodeDataResult | null => { - const nodeData = useContext(ClickedNodeContext); - if (nodeData === undefined) { - throw Error('useClickedNode must be used within a ClickedNode context'); - } - return nodeData; -}; diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx new file mode 100644 index 00000000000..647ef96c0ec --- /dev/null +++ b/react-components/src/hooks/useClickedNode.tsx @@ -0,0 +1,26 @@ +/*! + * Copyright 2023 Cognite AS + */ + +import { PointerEventData } from '@cognite/reveal'; +import { FdmAssetMappingsConfig, useReveal } from '../'; +import { useEffect, useState } from 'react'; +import { useNodeMappedData } from '../components/Reveal3DResources/useNodeMappedData'; + +export const useClickedNode = (fdmConfig?: FdmAssetMappingsConfig) => { + const viewer = useReveal(); + + const [lastClickEvent, setLastClickEvent] = useState(undefined); + + useEffect(() => { + const callback = (event: PointerEventData) => { + setLastClickEvent(event); + }; + + viewer.on('click', callback); + + return () => viewer.off('click', callback); + }, [viewer]); + + return useNodeMappedData(lastClickEvent, fdmConfig); +} diff --git a/react-components/src/index.ts b/react-components/src/index.ts index 40b21857121..901be470d51 100644 --- a/react-components/src/index.ts +++ b/react-components/src/index.ts @@ -18,10 +18,10 @@ export { RevealToolbar } from './components/RevealToolbar/RevealToolbar'; export { useReveal } from './components/RevealContainer/RevealContext'; export { use3DModelName } from './hooks/use3DModelName'; export { useFdmAssetMappings } from './hooks/useFdmAssetMappings'; +export { useClickedNode } from './hooks/useClickedNode'; // Higher order components export { withSuppressRevealEvents } from './higher-order-components/withSuppressRevealEvents'; -export { useClickedNode } from './higher-order-components/ClickedNode'; // Types export { diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx index 1664f89a1e4..6350a542ba8 100644 --- a/react-components/stories/ClickedNode.stories.tsx +++ b/react-components/stories/ClickedNode.stories.tsx @@ -3,10 +3,10 @@ */ import type { Meta, StoryObj } from '@storybook/react'; -import { Reveal3DResources, RevealContainer, RevealToolbar, useClickedNode } from '../src'; +import { FdmAssetMappingsConfig, Reveal3DResources, RevealContainer, RevealToolbar, useClickedNode, /* useClickedNode */ } from '../src'; import { CogniteClient } from '@cognite/sdk'; import { Color } from 'three'; -import { ClickedNode } from '../src/higher-order-components/ClickedNode'; +// import { ClickedNode } from '../src/higher-order-components/ClickedNode'; import { type ReactElement, useEffect } from 'react'; import { DefaultFdmConfig } from './utilities/fdmConfig'; @@ -43,17 +43,15 @@ export const Main: Story = { return ( - - - - + + ); } }; -const ClickedNodePrinter = (): ReactElement => { - const clickedNode = useClickedNode(); +const ClickedNodePrinter = ({ fdmConfig }: { fdmConfig: FdmAssetMappingsConfig | undefined } ): ReactElement => { + const clickedNode = useClickedNode(fdmConfig); useEffect(() => { console.log('Clicked node is ', clickedNode); }, [clickedNode]); From ad72a6f3574cd345dce30e7756e3c0139b149902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 8 Aug 2023 13:17:53 +0200 Subject: [PATCH 06/23] fix: correct query hash keys --- .../src/components/Reveal3DResources/useNodeMappedData.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx b/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx index 5a3c30336d6..f30ce2e60b1 100644 --- a/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx +++ b/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx @@ -96,7 +96,7 @@ function useAncestorNodesForTreeIndex( const cogniteClient = useSDK(); const queryResult = useQuery( - ['cdf', '3d', 'tree-index-to-ancestors', `${model?.modelId}-${model?.revisionId}-treeIndex`], + ['cdf', '3d', 'tree-index-to-ancestors', `${model?.modelId}-${model?.revisionId}-${treeIndex}`], async () => { assert(model !== undefined && treeIndex !== undefined); @@ -178,8 +178,7 @@ function useInspectNode( const fdmClient = useFdmSdk(); const inspectionResult = useQuery( - ['fdm', '3d', `inspect-${dataNode?.space}-${dataNode?.space}`], - + ['fdm', '3d', `inspect-${dataNode?.space}-${dataNode?.externalId}`], () => { assert(dataNode !== undefined); From 2592d97b7dd1577798b3986b05569538b2ac18ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 8 Aug 2023 13:28:21 +0200 Subject: [PATCH 07/23] chore: make sure story hook doesn't do unnecessary updates --- react-components/stories/ClickedNode.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx index 6350a542ba8..461b2ff7c90 100644 --- a/react-components/stories/ClickedNode.stories.tsx +++ b/react-components/stories/ClickedNode.stories.tsx @@ -54,7 +54,7 @@ const ClickedNodePrinter = ({ fdmConfig }: { fdmConfig: FdmAssetMappingsConfig | const clickedNode = useClickedNode(fdmConfig); useEffect(() => { console.log('Clicked node is ', clickedNode); - }, [clickedNode]); + }, [clickedNode?.nodeExternalId]); return <>; }; From d4bb156eb09b184ad74123af7312520da6329f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 8 Aug 2023 13:57:55 +0200 Subject: [PATCH 08/23] chore: lint fix --- .../Reveal3DResources/Reveal3DResources.tsx | 3 +- react-components/src/hooks/useClickedNode.tsx | 18 ++-- .../useNodeMappedData.tsx | 102 ++++++++++-------- .../stories/ClickedNode.stories.tsx | 16 ++- 4 files changed, 84 insertions(+), 55 deletions(-) rename react-components/src/{components/Reveal3DResources => hooks}/useNodeMappedData.tsx (67%) diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index b972217291a..5f7b928c8a2 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -26,8 +26,7 @@ import { import { type CogniteExternalId } from '@cognite/sdk'; import { type FdmAssetMappingsConfig } from '../../hooks/types'; import { useCalculateModelsStyling } from '../../hooks/useCalculateModelsStyling'; -import { useNodeMappedData } from './useNodeMappedData'; -import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; +import { useNodeMappedData } from '../../hooks/useNodeMappedData'; export type FdmAssetStylingGroup = { fdmAssetExternalIds: CogniteExternalId[]; diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index 647ef96c0ec..c481961095b 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -2,25 +2,29 @@ * Copyright 2023 Cognite AS */ -import { PointerEventData } from '@cognite/reveal'; -import { FdmAssetMappingsConfig, useReveal } from '../'; +import { type PointerEventData } from '@cognite/reveal'; +import { type FdmAssetMappingsConfig, useReveal, type NodeDataResult } from '../'; import { useEffect, useState } from 'react'; -import { useNodeMappedData } from '../components/Reveal3DResources/useNodeMappedData'; +import { useNodeMappedData } from './useNodeMappedData'; -export const useClickedNode = (fdmConfig?: FdmAssetMappingsConfig) => { +export const useClickedNode = ( + fdmConfig?: FdmAssetMappingsConfig | undefined +): NodeDataResult | undefined => { const viewer = useReveal(); const [lastClickEvent, setLastClickEvent] = useState(undefined); useEffect(() => { - const callback = (event: PointerEventData) => { + const callback = (event: PointerEventData): void => { setLastClickEvent(event); }; viewer.on('click', callback); - return () => viewer.off('click', callback); + return () => { + viewer.off('click', callback); + }; }, [viewer]); return useNodeMappedData(lastClickEvent, fdmConfig); -} +}; diff --git a/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx similarity index 67% rename from react-components/src/components/Reveal3DResources/useNodeMappedData.tsx rename to react-components/src/hooks/useNodeMappedData.tsx index f30ce2e60b1..2af51bed88f 100644 --- a/react-components/src/components/Reveal3DResources/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -6,31 +6,28 @@ import { useQuery } from '@tanstack/react-query'; import { type PointerEventData, type CogniteCadModel, type CadIntersection } from '@cognite/reveal'; import { type CogniteInternalId, type Node3D } from '@cognite/sdk'; -import { - type EdgeItem, - type InspectResultList, - type DmsUniqueIdentifier -} from '../../utilities/FdmSDK'; -import { type FdmAssetMappingsConfig } from '../../hooks/types'; -import { type NodeDataResult } from './types'; -import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; +import { type NodeDataResult } from '../components/Reveal3DResources/types'; +import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; import { useEffect, useState } from 'react'; -import { useReveal } from '../..'; +import { type FdmAssetMappingsConfig, useReveal } from '..'; import assert from 'assert'; +import { + type DmsUniqueIdentifier, + type EdgeItem, + type InspectResultList +} from '../utilities/FdmSDK'; export const useNodeMappedData = ( clickEvent: PointerEventData | undefined, - fdmConfig?: FdmAssetMappingsConfig + fdmConfig: FdmAssetMappingsConfig | undefined ): NodeDataResult | undefined => { - const viewer = useReveal(); const [cadIntersection, setCadIntersection] = useState(undefined); useEffect(() => { - (async () => { - + void (async () => { if (clickEvent === undefined) { return; } @@ -49,20 +46,25 @@ export const useNodeMappedData = ( })(); }, [clickEvent]); - const ancestors = useAncestorNodesForTreeIndex(cadIntersection?.model, cadIntersection?.treeIndex); + const ancestors = useAncestorNodesForTreeIndex( + cadIntersection?.model, + cadIntersection?.treeIndex + ); const mappings = useNodeMappingEdges( fdmConfig, cadIntersection?.model, - ancestors?.map(n => n.id) + ancestors?.map((n) => n.id) ); - const selectedEdge = mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; + const selectedEdge = + mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; const selectedNodeId = - fdmConfig === undefined ? undefined : - selectedEdge?.properties[fdmConfig.source.space][ - `${fdmConfig?.source.externalId}/${fdmConfig.source.version}` - ].revisionNodeId; + fdmConfig === undefined + ? undefined + : selectedEdge?.properties[fdmConfig.source.space][ + `${fdmConfig?.source.externalId}/${fdmConfig.source.version}` + ].revisionNodeId; const dataNode = selectedEdge?.startNode; @@ -73,10 +75,12 @@ export const useNodeMappedData = ( const selectedNode = ancestors?.find((n) => n.id === selectedNodeId); - if (selectedNode === undefined || + if ( + selectedNode === undefined || dataView === undefined || dataNode === undefined || - cadIntersection === undefined) { + cadIntersection === undefined + ) { return undefined; } @@ -86,17 +90,18 @@ export const useNodeMappedData = ( cadNode: selectedNode, intersection: cadIntersection }; -} +}; function useAncestorNodesForTreeIndex( model: CogniteCadModel | undefined, treeIndex: number | undefined ): Node3D[] | undefined { - const cogniteClient = useSDK(); + const nodeHashKey = `${model?.modelId ?? 0}-${model?.revisionId ?? 0}-${treeIndex ?? 0}`; + const queryResult = useQuery( - ['cdf', '3d', 'tree-index-to-ancestors', `${model?.modelId}-${model?.revisionId}-${treeIndex}`], + ['cdf', '3d', 'tree-index-to-ancestors', nodeHashKey], async () => { assert(model !== undefined && treeIndex !== undefined); @@ -110,7 +115,8 @@ function useAncestorNodesForTreeIndex( return ancestorNodes.items; }, - { enabled: model !== undefined && treeIndex !== undefined }); + { enabled: model !== undefined && treeIndex !== undefined } + ); return queryResult.data; } @@ -120,14 +126,17 @@ function useNodeMappingEdges( model: CogniteCadModel | undefined, ancestorIds: CogniteInternalId[] | undefined ): { edges: Array>> } | undefined { - const fdmClient = useFdmSdk(); const queryResult = useQuery( ['fdm', '3d', 'node-mapping-edges', ancestorIds], async () => { - - assert(fdmConfig !== undefined && model !== undefined && ancestorIds !== undefined && ancestorIds.length !== 0); + assert( + fdmConfig !== undefined && + model !== undefined && + ancestorIds !== undefined && + ancestorIds.length !== 0 + ); const filter = { and: [ @@ -163,26 +172,31 @@ function useNodeMappingEdges( ] }; - return fdmClient.filterAllInstances(filter, 'edge', fdmConfig.source); - }, { - enabled: fdmConfig !== undefined && model !== undefined && ancestorIds !== undefined && ancestorIds.length !== 0 - }); + return await fdmClient.filterAllInstances(filter, 'edge', fdmConfig.source); + }, + { + enabled: + fdmConfig !== undefined && + model !== undefined && + ancestorIds !== undefined && + ancestorIds.length !== 0 + } + ); return queryResult.data; } -function useInspectNode( - dataNode: DmsUniqueIdentifier | undefined -): InspectResultList | undefined { - +function useInspectNode(dataNode: DmsUniqueIdentifier | undefined): InspectResultList | undefined { const fdmClient = useFdmSdk(); + const nodeHashKey = `${dataNode?.space ?? ''}-${dataNode?.externalId ?? ''}`; + const inspectionResult = useQuery( - ['fdm', '3d', `inspect-${dataNode?.space}-${dataNode?.externalId}`], - () => { + ['fdm', '3d', 'inspect', nodeHashKey], + async () => { assert(dataNode !== undefined); - return fdmClient.inspectInstances({ + return await fdmClient.inspectInstances({ inspectionOperations: { involvedViewsAndContainers: {} }, items: [ { @@ -191,10 +205,12 @@ function useInspectNode( space: dataNode.space } ] - }) - }, { + }); + }, + { enabled: dataNode !== undefined - }); + } + ); return inspectionResult.data; } diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx index 461b2ff7c90..b61e3db33e5 100644 --- a/react-components/stories/ClickedNode.stories.tsx +++ b/react-components/stories/ClickedNode.stories.tsx @@ -3,7 +3,13 @@ */ import type { Meta, StoryObj } from '@storybook/react'; -import { FdmAssetMappingsConfig, Reveal3DResources, RevealContainer, RevealToolbar, useClickedNode, /* useClickedNode */ } from '../src'; +import { + type FdmAssetMappingsConfig, + Reveal3DResources, + RevealContainer, + RevealToolbar, + useClickedNode /* useClickedNode */ +} from '../src'; import { CogniteClient } from '@cognite/sdk'; import { Color } from 'three'; // import { ClickedNode } from '../src/higher-order-components/ClickedNode'; @@ -43,14 +49,18 @@ export const Main: Story = { return ( - + ); } }; -const ClickedNodePrinter = ({ fdmConfig }: { fdmConfig: FdmAssetMappingsConfig | undefined } ): ReactElement => { +const ClickedNodePrinter = ({ + fdmConfig +}: { + fdmConfig: FdmAssetMappingsConfig | undefined; +}): ReactElement => { const clickedNode = useClickedNode(fdmConfig); useEffect(() => { console.log('Clicked node is ', clickedNode); From 7e4aeb292bcb3e7b8ae110804ba034a6e83c308f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 8 Aug 2023 14:22:41 +0200 Subject: [PATCH 09/23] fix: return promise as promised --- .../src/components/Reveal3DResources/Reveal3DResources.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 5f7b928c8a2..58ceeeb9850 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -75,7 +75,7 @@ export const Reveal3DResources = ({ useEffect(() => { if (clickedNodeData !== undefined) { - onNodeClick?.(clickedNodeData); + onNodeClick?.(Promise.resolve(clickedNodeData)); } }, [lastClickEvent, clickedNodeData]); From 765a791d95958d66ab2a6692755c397d38614a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 8 Aug 2023 16:02:51 +0200 Subject: [PATCH 10/23] refactor: refactor hook to not deal with intersection logic --- .../src/components/Reveal3DResources/types.ts | 1 - react-components/src/hooks/useClickedNode.tsx | 34 +++++++++++++++--- .../src/hooks/useNodeMappedData.tsx | 35 ++++--------------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/react-components/src/components/Reveal3DResources/types.ts b/react-components/src/components/Reveal3DResources/types.ts index bb30b2a84ce..0c7d6714cd4 100644 --- a/react-components/src/components/Reveal3DResources/types.ts +++ b/react-components/src/components/Reveal3DResources/types.ts @@ -26,5 +26,4 @@ export type NodeDataResult = { nodeExternalId: string; view: Source; cadNode: Node3D; - intersection: CadIntersection; }; diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index c481961095b..470a21377c6 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -2,21 +2,40 @@ * Copyright 2023 Cognite AS */ -import { type PointerEventData } from '@cognite/reveal'; +import { CadIntersection, type PointerEventData } from '@cognite/reveal'; import { type FdmAssetMappingsConfig, useReveal, type NodeDataResult } from '../'; import { useEffect, useState } from 'react'; import { useNodeMappedData } from './useNodeMappedData'; +export type ClickedNodeData = NodeDataResult & { + intersection: CadIntersection; +} + export const useClickedNode = ( fdmConfig?: FdmAssetMappingsConfig | undefined -): NodeDataResult | undefined => { +): ClickedNodeData | undefined => { const viewer = useReveal(); - const [lastClickEvent, setLastClickEvent] = useState(undefined); + const [cadIntersection, setCadIntersection] = useState(undefined); useEffect(() => { const callback = (event: PointerEventData): void => { - setLastClickEvent(event); + void (async () => { + if (event === undefined) { + return; + } + + const intersection = await viewer.getIntersectionFromPixel( + event.offsetX, + event.offsetY + ); + + if (intersection === null || intersection.type !== 'cad') { + return; + } + + setCadIntersection(intersection); + })(); }; viewer.on('click', callback); @@ -26,5 +45,10 @@ export const useClickedNode = ( }; }, [viewer]); - return useNodeMappedData(lastClickEvent, fdmConfig); + const nodeData = useNodeMappedData(cadIntersection?.treeIndex, cadIntersection?.model, fdmConfig); + + if (nodeData === undefined || cadIntersection === undefined) { + return undefined; + } + return { intersection: cadIntersection, ...nodeData }; }; diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx index 2af51bed88f..8c251e3f1f9 100644 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -19,41 +19,19 @@ import { } from '../utilities/FdmSDK'; export const useNodeMappedData = ( - clickEvent: PointerEventData | undefined, + treeIndex: number | undefined, + model: CogniteCadModel | undefined, fdmConfig: FdmAssetMappingsConfig | undefined ): NodeDataResult | undefined => { - const viewer = useReveal(); - - const [cadIntersection, setCadIntersection] = useState(undefined); - - useEffect(() => { - void (async () => { - if (clickEvent === undefined) { - return; - } - - const intersection = await viewer.getIntersectionFromPixel( - clickEvent.offsetX, - clickEvent.offsetY - ); - - if (intersection === null || intersection.type !== 'cad') { - return; - } - - const cadIntersection = intersection; - setCadIntersection(cadIntersection); - })(); - }, [clickEvent]); const ancestors = useAncestorNodesForTreeIndex( - cadIntersection?.model, - cadIntersection?.treeIndex + model, + treeIndex ); const mappings = useNodeMappingEdges( fdmConfig, - cadIntersection?.model, + model, ancestors?.map((n) => n.id) ); @@ -79,7 +57,8 @@ export const useNodeMappedData = ( selectedNode === undefined || dataView === undefined || dataNode === undefined || - cadIntersection === undefined + model === undefined || + treeIndex === undefined ) { return undefined; } From e461cab05d64ff7a23585994408cf3e91aadf4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 9 Aug 2023 10:38:44 +0200 Subject: [PATCH 11/23] chore: lint fix --- .../src/components/Reveal3DResources/types.ts | 6 +---- react-components/src/hooks/useClickedNode.tsx | 9 +++---- .../src/hooks/useNodeMappedData.tsx | 24 ++++++------------- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/react-components/src/components/Reveal3DResources/types.ts b/react-components/src/components/Reveal3DResources/types.ts index 0c7d6714cd4..6602ca22152 100644 --- a/react-components/src/components/Reveal3DResources/types.ts +++ b/react-components/src/components/Reveal3DResources/types.ts @@ -2,11 +2,7 @@ * Copyright 2023 Cognite AS */ -import { - type AddModelOptions, - type SupportedModelTypes, - type CadIntersection -} from '@cognite/reveal'; +import { type AddModelOptions, type SupportedModelTypes } from '@cognite/reveal'; import { type Matrix4 } from 'three'; import { type Source } from '../../utilities/FdmSDK'; import { type Node3D } from '@cognite/sdk/dist/src'; diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index 470a21377c6..ae80e3a4531 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -2,14 +2,14 @@ * Copyright 2023 Cognite AS */ -import { CadIntersection, type PointerEventData } from '@cognite/reveal'; +import { type CadIntersection, type PointerEventData } from '@cognite/reveal'; import { type FdmAssetMappingsConfig, useReveal, type NodeDataResult } from '../'; import { useEffect, useState } from 'react'; import { useNodeMappedData } from './useNodeMappedData'; export type ClickedNodeData = NodeDataResult & { intersection: CadIntersection; -} +}; export const useClickedNode = ( fdmConfig?: FdmAssetMappingsConfig | undefined @@ -25,10 +25,7 @@ export const useClickedNode = ( return; } - const intersection = await viewer.getIntersectionFromPixel( - event.offsetX, - event.offsetY - ); + const intersection = await viewer.getIntersectionFromPixel(event.offsetX, event.offsetY); if (intersection === null || intersection.type !== 'cad') { return; diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx index 8c251e3f1f9..f6a2cd33486 100644 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -4,12 +4,11 @@ import { useQuery } from '@tanstack/react-query'; -import { type PointerEventData, type CogniteCadModel, type CadIntersection } from '@cognite/reveal'; +import { type CogniteCadModel } from '@cognite/reveal'; import { type CogniteInternalId, type Node3D } from '@cognite/sdk'; import { type NodeDataResult } from '../components/Reveal3DResources/types'; import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; -import { useEffect, useState } from 'react'; -import { type FdmAssetMappingsConfig, useReveal } from '..'; +import { type FdmAssetMappingsConfig } from '..'; import assert from 'assert'; import { @@ -23,17 +22,9 @@ export const useNodeMappedData = ( model: CogniteCadModel | undefined, fdmConfig: FdmAssetMappingsConfig | undefined ): NodeDataResult | undefined => { + const ancestors = useAncestorNodesForTreeIndex(model, treeIndex); - const ancestors = useAncestorNodesForTreeIndex( - model, - treeIndex - ); - - const mappings = useNodeMappingEdges( - fdmConfig, - model, - ancestors?.map((n) => n.id) - ); + const mappings = useNodeMappingEdges(fdmConfig, model, ancestors?.map((n) => n.id)); const selectedEdge = mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; @@ -57,8 +48,8 @@ export const useNodeMappedData = ( selectedNode === undefined || dataView === undefined || dataNode === undefined || - model === undefined || - treeIndex === undefined + model === undefined || + treeIndex === undefined ) { return undefined; } @@ -66,8 +57,7 @@ export const useNodeMappedData = ( return { nodeExternalId: dataNode.externalId, view: dataView, - cadNode: selectedNode, - intersection: cadIntersection + cadNode: selectedNode }; }; From 291ad0e051981700d0245bf2e6964e84acb5c3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 9 Aug 2023 11:04:32 +0200 Subject: [PATCH 12/23] fix: build fix --- .../Reveal3DResources/Reveal3DResources.tsx | 23 +++------------ .../stories/HighlightNode.stories.tsx | 28 ++++++------------- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 58ceeeb9850..49184d21cd7 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -5,8 +5,7 @@ import { useRef, type ReactElement, useContext, useState, useEffect } from 'reac import { type NodeAppearance, type Cognite3DViewer, - type PointCloudAppearance, - type PointerEventData + type PointCloudAppearance } from '@cognite/reveal'; import { ModelsLoadingStateContext } from './ModelsLoadingContext'; import { CadModelContainer, type CadModelStyling } from '../CadModelContainer/CadModelContainer'; @@ -26,7 +25,7 @@ import { import { type CogniteExternalId } from '@cognite/sdk'; import { type FdmAssetMappingsConfig } from '../../hooks/types'; import { useCalculateModelsStyling } from '../../hooks/useCalculateModelsStyling'; -import { useNodeMappedData } from '../../hooks/useNodeMappedData'; +import { useClickedNode } from '../..'; export type FdmAssetStylingGroup = { fdmAssetExternalIds: CogniteExternalId[]; @@ -60,14 +59,12 @@ export const Reveal3DResources = ({ const viewer = useReveal(); const numModelsLoaded = useRef(0); - const [lastClickEvent, setLastClickEvent] = useState(undefined); - useEffect(() => { getTypedModels(resources, viewer).then(setReveal3DModels).catch(console.error); }, [resources, viewer]); const modelsStyling = useCalculateModelsStyling(reveal3DModels, styling, fdmAssetMappingConfig); - const clickedNodeData = useNodeMappedData(lastClickEvent, fdmAssetMappingConfig); + const clickedNodeData = useClickedNode(fdmAssetMappingConfig); useEffect(() => { setReveal3DModelsStyling(modelsStyling); @@ -77,19 +74,7 @@ export const Reveal3DResources = ({ if (clickedNodeData !== undefined) { onNodeClick?.(Promise.resolve(clickedNodeData)); } - }, [lastClickEvent, clickedNodeData]); - - useEffect(() => { - const callback = (event: PointerEventData): void => { - setLastClickEvent(event); - }; - - viewer.on('click', callback); - - return () => { - viewer.off('click', callback); - }; - }, [viewer, onNodeClick]); + }, [clickedNodeData, onNodeClick]); const image360CollectionAddOptions = resources.filter( (resource): resource is AddImageCollection360Options => diff --git a/react-components/stories/HighlightNode.stories.tsx b/react-components/stories/HighlightNode.stories.tsx index 1cee63c3f01..9ce21668f22 100644 --- a/react-components/stories/HighlightNode.stories.tsx +++ b/react-components/stories/HighlightNode.stories.tsx @@ -8,12 +8,12 @@ import { RevealContainer, RevealToolbar, Reveal3DResources, - type NodeDataResult, - type AddResourceOptions + type AddResourceOptions, + useClickedNode } from '../src'; import { Color, Matrix4 } from 'three'; -import { type ReactElement, useState } from 'react'; -import { DefaultNodeAppearance, TreeIndexNodeCollection } from '@cognite/reveal'; +import { type ReactElement, useState, useEffect } from 'react'; +import { DefaultNodeAppearance } from '@cognite/reveal'; import { createSdkByUrlToken } from './utilities/createSdkByUrlToken'; import { DefaultFdmConfig } from './utilities/fdmConfig'; @@ -56,22 +56,13 @@ const StoryContent = ({ resources: AddResourceOptions[]; fdmAssetMappingConfig?: FdmAssetMappingsConfig; }): ReactElement => { - const [nodeData, setNodeData] = useState(undefined); - const [highlightedId, setHighlightedId] = useState(undefined); - const callback = async (nodeData: Promise): Promise => { - const nodeDataResult = await nodeData; - setNodeData(nodeDataResult); - setHighlightedId(nodeDataResult?.nodeExternalId); - - if (nodeDataResult === undefined) return; + const nodeData = useClickedNode(fdmAssetMappingConfig); - nodeDataResult.intersection.model.assignStyledNodeCollection( - new TreeIndexNodeCollection([nodeDataResult.cadNode.treeIndex]), - DefaultNodeAppearance.Highlighted - ); - }; + useEffect(() => { + setHighlightedId(nodeData?.nodeExternalId); + }, [nodeData?.nodeExternalId]); return ( <> @@ -89,9 +80,6 @@ const StoryContent = ({ ] }} fdmAssetMappingConfig={fdmAssetMappingConfig} - onNodeClick={(nodeData) => { - void callback(nodeData); - }} /> NodeData is: {JSON.stringify(nodeData)} From bcbf697b144d80aad540fe5e2fe0aa1ff6c6d4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 9 Aug 2023 12:31:42 +0200 Subject: [PATCH 13/23] chore: remove unnecessary check --- react-components/src/hooks/useClickedNode.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index ae80e3a4531..c4e46b303e7 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -21,10 +21,6 @@ export const useClickedNode = ( useEffect(() => { const callback = (event: PointerEventData): void => { void (async () => { - if (event === undefined) { - return; - } - const intersection = await viewer.getIntersectionFromPixel(event.offsetX, event.offsetY); if (intersection === null || intersection.type !== 'cad') { From 0b6008d34e46adec9658fe6cec1e5533f9c92add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 9 Aug 2023 12:31:51 +0200 Subject: [PATCH 14/23] chore: remove unused comments --- react-components/stories/ClickedNode.stories.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx index b61e3db33e5..32ff2172a8a 100644 --- a/react-components/stories/ClickedNode.stories.tsx +++ b/react-components/stories/ClickedNode.stories.tsx @@ -8,11 +8,10 @@ import { Reveal3DResources, RevealContainer, RevealToolbar, - useClickedNode /* useClickedNode */ + useClickedNode } from '../src'; import { CogniteClient } from '@cognite/sdk'; import { Color } from 'three'; -// import { ClickedNode } from '../src/higher-order-components/ClickedNode'; import { type ReactElement, useEffect } from 'react'; import { DefaultFdmConfig } from './utilities/fdmConfig'; From 53e0d05bcb655879bfeb7167b1bbe9ef30927aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 14 Aug 2023 10:30:26 +0200 Subject: [PATCH 15/23] refactor: rename to useClickedNodeData --- .../src/components/Reveal3DResources/Reveal3DResources.tsx | 4 ++-- react-components/src/hooks/useClickedNode.tsx | 2 +- react-components/src/index.ts | 2 +- react-components/stories/ClickedNode.stories.tsx | 4 ++-- react-components/stories/HighlightNode.stories.tsx | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index 49184d21cd7..1c0d6863bd0 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -25,7 +25,7 @@ import { import { type CogniteExternalId } from '@cognite/sdk'; import { type FdmAssetMappingsConfig } from '../../hooks/types'; import { useCalculateModelsStyling } from '../../hooks/useCalculateModelsStyling'; -import { useClickedNode } from '../..'; +import { useClickedNodeData } from '../..'; export type FdmAssetStylingGroup = { fdmAssetExternalIds: CogniteExternalId[]; @@ -64,7 +64,7 @@ export const Reveal3DResources = ({ }, [resources, viewer]); const modelsStyling = useCalculateModelsStyling(reveal3DModels, styling, fdmAssetMappingConfig); - const clickedNodeData = useClickedNode(fdmAssetMappingConfig); + const clickedNodeData = useClickedNodeData(fdmAssetMappingConfig); useEffect(() => { setReveal3DModelsStyling(modelsStyling); diff --git a/react-components/src/hooks/useClickedNode.tsx b/react-components/src/hooks/useClickedNode.tsx index c4e46b303e7..3b883da6b3d 100644 --- a/react-components/src/hooks/useClickedNode.tsx +++ b/react-components/src/hooks/useClickedNode.tsx @@ -11,7 +11,7 @@ export type ClickedNodeData = NodeDataResult & { intersection: CadIntersection; }; -export const useClickedNode = ( +export const useClickedNodeData = ( fdmConfig?: FdmAssetMappingsConfig | undefined ): ClickedNodeData | undefined => { const viewer = useReveal(); diff --git a/react-components/src/index.ts b/react-components/src/index.ts index 901be470d51..c89d0aa5d82 100644 --- a/react-components/src/index.ts +++ b/react-components/src/index.ts @@ -18,7 +18,7 @@ export { RevealToolbar } from './components/RevealToolbar/RevealToolbar'; export { useReveal } from './components/RevealContainer/RevealContext'; export { use3DModelName } from './hooks/use3DModelName'; export { useFdmAssetMappings } from './hooks/useFdmAssetMappings'; -export { useClickedNode } from './hooks/useClickedNode'; +export { useClickedNodeData } from './hooks/useClickedNode'; // Higher order components export { withSuppressRevealEvents } from './higher-order-components/withSuppressRevealEvents'; diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx index 32ff2172a8a..81122345ac1 100644 --- a/react-components/stories/ClickedNode.stories.tsx +++ b/react-components/stories/ClickedNode.stories.tsx @@ -8,7 +8,7 @@ import { Reveal3DResources, RevealContainer, RevealToolbar, - useClickedNode + useClickedNodeData } from '../src'; import { CogniteClient } from '@cognite/sdk'; import { Color } from 'three'; @@ -60,7 +60,7 @@ const ClickedNodePrinter = ({ }: { fdmConfig: FdmAssetMappingsConfig | undefined; }): ReactElement => { - const clickedNode = useClickedNode(fdmConfig); + const clickedNode = useClickedNodeData(fdmConfig); useEffect(() => { console.log('Clicked node is ', clickedNode); }, [clickedNode?.nodeExternalId]); diff --git a/react-components/stories/HighlightNode.stories.tsx b/react-components/stories/HighlightNode.stories.tsx index 9ce21668f22..9d1a56b4039 100644 --- a/react-components/stories/HighlightNode.stories.tsx +++ b/react-components/stories/HighlightNode.stories.tsx @@ -9,7 +9,7 @@ import { RevealToolbar, Reveal3DResources, type AddResourceOptions, - useClickedNode + useClickedNodeData } from '../src'; import { Color, Matrix4 } from 'three'; import { type ReactElement, useState, useEffect } from 'react'; @@ -58,7 +58,7 @@ const StoryContent = ({ }): ReactElement => { const [highlightedId, setHighlightedId] = useState(undefined); - const nodeData = useClickedNode(fdmAssetMappingConfig); + const nodeData = useClickedNodeData(fdmAssetMappingConfig); useEffect(() => { setHighlightedId(nodeData?.nodeExternalId); From c09d3e0becef58b16b67b104d9cff467d6006d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 14 Aug 2023 11:28:49 +0200 Subject: [PATCH 16/23] fix: avoid stringification of circular JSON struct --- react-components/stories/HighlightNode.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-components/stories/HighlightNode.stories.tsx b/react-components/stories/HighlightNode.stories.tsx index 9d1a56b4039..83e6d11bfc1 100644 --- a/react-components/stories/HighlightNode.stories.tsx +++ b/react-components/stories/HighlightNode.stories.tsx @@ -82,7 +82,7 @@ const StoryContent = ({ fdmAssetMappingConfig={fdmAssetMappingConfig} /> - NodeData is: {JSON.stringify(nodeData)} + NodeData is: {JSON.stringify(nodeData?.nodeExternalId)} ); }; From a17c8515b61d363f025e8f6cd931da8ff469c9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 14 Aug 2023 13:05:54 +0200 Subject: [PATCH 17/23] refactor: combine all react queries into one --- .../src/hooks/useNodeMappedData.tsx | 249 ++++++++---------- 1 file changed, 115 insertions(+), 134 deletions(-) diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx index f6a2cd33486..370f8a6f265 100644 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -5,13 +5,14 @@ import { useQuery } from '@tanstack/react-query'; import { type CogniteCadModel } from '@cognite/reveal'; -import { type CogniteInternalId, type Node3D } from '@cognite/sdk'; +import { CogniteClient, type CogniteInternalId, type Node3D } from '@cognite/sdk'; import { type NodeDataResult } from '../components/Reveal3DResources/types'; import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; import { type FdmAssetMappingsConfig } from '..'; import assert from 'assert'; import { + FdmSDK, type DmsUniqueIdentifier, type EdgeItem, type InspectResultList @@ -22,164 +23,144 @@ export const useNodeMappedData = ( model: CogniteCadModel | undefined, fdmConfig: FdmAssetMappingsConfig | undefined ): NodeDataResult | undefined => { - const ancestors = useAncestorNodesForTreeIndex(model, treeIndex); - const mappings = useNodeMappingEdges(fdmConfig, model, ancestors?.map((n) => n.id)); + const cogniteClient = useSDK(); + const fdmClient = useFdmSdk(); + + const mappedDataHashKey = `${model?.modelId}-${model?.revisionId}-${treeIndex}`; + + const queryResult = useQuery( + ['cdf', '3d', mappedDataHashKey], + async () => { + + if (model === undefined || treeIndex === undefined) { + return null; + } + + const ancestors = await fetchAncestorNodesForTreeIndex(model, treeIndex, cogniteClient); + + if (ancestors.length === 0) { + return null; + } + + const mappings = await fetchNodeMappingEdges(fdmConfig, model, ancestors.map((n) => n.id), fdmClient); - const selectedEdge = - mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; - const selectedNodeId = - fdmConfig === undefined - ? undefined - : selectedEdge?.properties[fdmConfig.source.space][ + const selectedEdge = + mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; + + const selectedNodeId = + fdmConfig === undefined + ? undefined + : selectedEdge?.properties[fdmConfig.source.space][ `${fdmConfig?.source.externalId}/${fdmConfig.source.version}` ].revisionNodeId; - const dataNode = selectedEdge?.startNode; + const dataNode = selectedEdge?.startNode; - const inspectionResult = useInspectNode(dataNode); + if (dataNode === undefined) { + return null; + } - const dataView = - inspectionResult?.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; + const inspectionResult = await inspectNode(dataNode, fdmClient); - const selectedNode = ancestors?.find((n) => n.id === selectedNodeId); + const dataView = + inspectionResult?.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; - if ( - selectedNode === undefined || - dataView === undefined || - dataNode === undefined || - model === undefined || - treeIndex === undefined - ) { - return undefined; - } + if (dataView === undefined) { + return null; + } - return { - nodeExternalId: dataNode.externalId, - view: dataView, - cadNode: selectedNode - }; -}; + const selectedNode = ancestors.find((n) => n.id === selectedNodeId)!; -function useAncestorNodesForTreeIndex( - model: CogniteCadModel | undefined, - treeIndex: number | undefined -): Node3D[] | undefined { - const cogniteClient = useSDK(); - - const nodeHashKey = `${model?.modelId ?? 0}-${model?.revisionId ?? 0}-${treeIndex ?? 0}`; + return { + nodeExternalId: dataNode.externalId, + view: dataView, + cadNode: selectedNode + }; + }); - const queryResult = useQuery( - ['cdf', '3d', 'tree-index-to-ancestors', nodeHashKey], - async () => { - assert(model !== undefined && treeIndex !== undefined); + return queryResult.data ?? undefined; +}; - const nodeId = await model.mapTreeIndexToNodeId(treeIndex); +async function fetchAncestorNodesForTreeIndex( + model: CogniteCadModel, + treeIndex: number, + cogniteClient: CogniteClient, +): Promise { - const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( - model.modelId, - model.revisionId, - nodeId - ); + const nodeId = await model.mapTreeIndexToNodeId(treeIndex); - return ancestorNodes.items; - }, - { enabled: model !== undefined && treeIndex !== undefined } + const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( + model.modelId, + model.revisionId, + nodeId ); - return queryResult.data; + return ancestorNodes.items; } -function useNodeMappingEdges( +async function fetchNodeMappingEdges( fdmConfig: FdmAssetMappingsConfig | undefined, - model: CogniteCadModel | undefined, - ancestorIds: CogniteInternalId[] | undefined -): { edges: Array>> } | undefined { - const fdmClient = useFdmSdk(); + model: CogniteCadModel, + ancestorIds: CogniteInternalId[], + fdmClient: FdmSDK +): Promise<{ edges: Array>> } | undefined> { - const queryResult = useQuery( - ['fdm', '3d', 'node-mapping-edges', ancestorIds], - async () => { - assert( - fdmConfig !== undefined && - model !== undefined && - ancestorIds !== undefined && - ancestorIds.length !== 0 - ); - - const filter = { - and: [ - { - equals: { - property: ['edge', 'endNode'], - value: { - space: fdmConfig.global3dSpace, - externalId: `model_3d_${model.modelId}` - } - } - }, - { - equals: { - property: [ - fdmConfig.source.space, - `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, - 'revisionId' - ], - value: model.revisionId - } - }, - { - in: { - property: [ - fdmConfig.source.space, - `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, - 'revisionNodeId' - ], - values: ancestorIds - } - } - ] - }; + if (fdmConfig === undefined) { + throw new Error('FdmConfig must be supplied for using FDM endpoints'); + } - return await fdmClient.filterAllInstances(filter, 'edge', fdmConfig.source); - }, - { - enabled: - fdmConfig !== undefined && - model !== undefined && - ancestorIds !== undefined && - ancestorIds.length !== 0 - } - ); + assert(ancestorIds.length !== 0); - return queryResult.data; -} + const filter = { + and: [ + { + equals: { + property: ['edge', 'endNode'], + value: { + space: fdmConfig.global3dSpace, + externalId: `model_3d_${model.modelId}` + } + } + }, + { + equals: { + property: [ + fdmConfig.source.space, + `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, + 'revisionId' + ], + value: model.revisionId + } + }, + { + in: { + property: [ + fdmConfig.source.space, + `${fdmConfig.source.externalId}/${fdmConfig.source.version}`, + 'revisionNodeId' + ], + values: ancestorIds + } + } + ] + }; -function useInspectNode(dataNode: DmsUniqueIdentifier | undefined): InspectResultList | undefined { - const fdmClient = useFdmSdk(); + return fdmClient.filterAllInstances(filter, 'edge', fdmConfig.source); +} - const nodeHashKey = `${dataNode?.space ?? ''}-${dataNode?.externalId ?? ''}`; +async function inspectNode(dataNode: DmsUniqueIdentifier, fdmClient: FdmSDK): Promise { - const inspectionResult = useQuery( - ['fdm', '3d', 'inspect', nodeHashKey], - async () => { - assert(dataNode !== undefined); - - return await fdmClient.inspectInstances({ - inspectionOperations: { involvedViewsAndContainers: {} }, - items: [ - { - instanceType: 'node', - externalId: dataNode.externalId, - space: dataNode.space - } - ] - }); - }, - { - enabled: dataNode !== undefined - } - ); + const inspectionResult = await fdmClient.inspectInstances({ + inspectionOperations: { involvedViewsAndContainers: {} }, + items: [ + { + instanceType: 'node', + externalId: dataNode.externalId, + space: dataNode.space + } + ] + }); - return inspectionResult.data; + return inspectionResult; } From 8831e63162bb29cbc34917b8d4a4db28b1dbe07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 14 Aug 2023 13:10:22 +0200 Subject: [PATCH 18/23] chore: Remove new story --- .../stories/ClickedNode.stories.tsx | 69 ------------------- 1 file changed, 69 deletions(-) delete mode 100644 react-components/stories/ClickedNode.stories.tsx diff --git a/react-components/stories/ClickedNode.stories.tsx b/react-components/stories/ClickedNode.stories.tsx deleted file mode 100644 index 81122345ac1..00000000000 --- a/react-components/stories/ClickedNode.stories.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ - -import type { Meta, StoryObj } from '@storybook/react'; -import { - type FdmAssetMappingsConfig, - Reveal3DResources, - RevealContainer, - RevealToolbar, - useClickedNodeData -} from '../src'; -import { CogniteClient } from '@cognite/sdk'; -import { Color } from 'three'; -import { type ReactElement, useEffect } from 'react'; - -import { DefaultFdmConfig } from './utilities/fdmConfig'; - -const meta = { - title: 'Example/ClickedNode', - component: Reveal3DResources, - tags: ['autodocs'] -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const token = new URLSearchParams(window.location.search).get('token') ?? ''; -const sdk = new CogniteClient({ - appId: 'reveal.example', - baseUrl: 'https://greenfield.cognitedata.com', - project: '3d-test', - getToken: async () => await Promise.resolve(token) -}); - -export const Main: Story = { - args: { - resources: [ - { - modelId: 2551525377383868, - revisionId: 2143672450453400 - } - ], - styling: {}, - fdmAssetMappingConfig: DefaultFdmConfig - }, - render: ({ resources, fdmAssetMappingConfig }) => { - return ( - - - - - - ); - } -}; - -const ClickedNodePrinter = ({ - fdmConfig -}: { - fdmConfig: FdmAssetMappingsConfig | undefined; -}): ReactElement => { - const clickedNode = useClickedNodeData(fdmConfig); - useEffect(() => { - console.log('Clicked node is ', clickedNode); - }, [clickedNode?.nodeExternalId]); - - return <>; -}; From 4abbbf2f8e074aee45e646f44d31c2960a48616e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 14 Aug 2023 14:03:21 +0200 Subject: [PATCH 19/23] fix: correct use of space names and properties --- react-components/src/hooks/useNodeMappedData.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx index f6a7e43e411..3d82ca9c84b 100644 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -16,7 +16,7 @@ import { type EdgeItem, type InspectResultList } from '../utilities/FdmSDK'; -import { INSTANCE_SPACE_3D_DATA, SYSTEM_3D_EDGE_SOURCE } from '../utilities/globalDataModels'; +import { INSTANCE_SPACE_3D_DATA, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../utilities/globalDataModels'; export const useNodeMappedData = ( treeIndex: number | undefined, @@ -47,11 +47,7 @@ export const useNodeMappedData = ( const selectedEdge = mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; - - - const selectedNodeId = selectedEdge?.properties[SYSTEM_3D_EDGE_SOURCE.space][ - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}` - ].revisionNodeId; + const selectedNodeId = selectedEdge?.properties.revisionNodeId; const dataNode = selectedEdge?.startNode; @@ -112,14 +108,14 @@ async function fetchNodeMappingEdges( property: ['edge', 'endNode'], value: { space: INSTANCE_SPACE_3D_DATA, - externalId: `model_3d_${model.modelId}` + externalId: `${model.modelId}` } } }, { equals: { property: [ - SYSTEM_3D_EDGE_SOURCE.space, + SYSTEM_SPACE_3D_SCHEMA, `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, 'revisionId' ], @@ -129,7 +125,7 @@ async function fetchNodeMappingEdges( { in: { property: [ - SYSTEM_3D_EDGE_SOURCE.space, + SYSTEM_SPACE_3D_SCHEMA, `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, 'revisionNodeId' ], From a0b9c990a0264e60aee78bf87dfefa641cde9f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Mon, 14 Aug 2023 14:41:44 +0200 Subject: [PATCH 20/23] chore: lint fix --- .../Reveal3DResources/Reveal3DResources.tsx | 8 +- .../src/components/Reveal3DResources/types.ts | 6 +- .../src/hooks/useNodeMappedData.tsx | 90 ++++++++++--------- .../stories/HighlightNode.stories.tsx | 23 +++-- 4 files changed, 64 insertions(+), 63 deletions(-) diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index a7c2371f0c5..e0636504e78 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -2,11 +2,7 @@ * Copyright 2023 Cognite AS */ import { useRef, type ReactElement, useContext, useState, useEffect } from 'react'; -import { - type NodeAppearance, - type Cognite3DViewer, - type PointCloudAppearance -} from '@cognite/reveal'; +import { type NodeAppearance, type Cognite3DViewer } from '@cognite/reveal'; import { ModelsLoadingStateContext } from './ModelsLoadingContext'; import { CadModelContainer, type CadModelStyling } from '../CadModelContainer/CadModelContainer'; import { @@ -22,8 +18,6 @@ import { type AddResourceOptions, type NodeDataResult } from './types'; -import { queryMappedData } from './queryMappedData'; -import { useFdmSdk, useSDK } from '../RevealContainer/SDKProvider'; import { useCalculateModelsStyling } from '../../hooks/useCalculateModelsStyling'; import { type DmsUniqueIdentifier } from '../../utilities/FdmSDK'; import { useClickedNodeData } from '../..'; diff --git a/react-components/src/components/Reveal3DResources/types.ts b/react-components/src/components/Reveal3DResources/types.ts index 87ca6068380..0b7b5a1441c 100644 --- a/react-components/src/components/Reveal3DResources/types.ts +++ b/react-components/src/components/Reveal3DResources/types.ts @@ -2,7 +2,11 @@ * Copyright 2023 Cognite AS */ -import { NodeAppearance, type AddModelOptions, type SupportedModelTypes } from '@cognite/reveal'; +import { + type NodeAppearance, + type AddModelOptions, + type SupportedModelTypes +} from '@cognite/reveal'; import { type Matrix4 } from 'three'; import { type Source } from '../../utilities/FdmSDK'; diff --git a/react-components/src/hooks/useNodeMappedData.tsx b/react-components/src/hooks/useNodeMappedData.tsx index 3d82ca9c84b..d1afa4dafe7 100644 --- a/react-components/src/hooks/useNodeMappedData.tsx +++ b/react-components/src/hooks/useNodeMappedData.tsx @@ -5,73 +5,79 @@ import { useQuery } from '@tanstack/react-query'; import { type CogniteCadModel } from '@cognite/reveal'; -import { CogniteClient, type CogniteInternalId, type Node3D } from '@cognite/sdk'; +import { type CogniteClient, type CogniteInternalId, type Node3D } from '@cognite/sdk'; import { type NodeDataResult } from '../components/Reveal3DResources/types'; import { useFdmSdk, useSDK } from '../components/RevealContainer/SDKProvider'; import assert from 'assert'; import { - FdmSDK, + type FdmSDK, type DmsUniqueIdentifier, type EdgeItem, type InspectResultList } from '../utilities/FdmSDK'; -import { INSTANCE_SPACE_3D_DATA, SYSTEM_3D_EDGE_SOURCE, SYSTEM_SPACE_3D_SCHEMA } from '../utilities/globalDataModels'; +import { + INSTANCE_SPACE_3D_DATA, + SYSTEM_3D_EDGE_SOURCE, + SYSTEM_SPACE_3D_SCHEMA +} from '../utilities/globalDataModels'; export const useNodeMappedData = ( treeIndex: number | undefined, model: CogniteCadModel | undefined ): NodeDataResult | undefined => { - const cogniteClient = useSDK(); const fdmClient = useFdmSdk(); - const mappedDataHashKey = `${model?.modelId}-${model?.revisionId}-${treeIndex}`; + const mappedDataHashKey = `${model?.modelId ?? ''}-${model?.revisionId ?? ''}-${treeIndex ?? ''}`; - const queryResult = useQuery( - ['cdf', '3d', mappedDataHashKey], - async () => { + const queryResult = useQuery(['cdf', '3d', mappedDataHashKey], async () => { + if (model === undefined || treeIndex === undefined) { + return null; + } - if (model === undefined || treeIndex === undefined) { - return null; - } + const ancestors = await fetchAncestorNodesForTreeIndex(model, treeIndex, cogniteClient); - const ancestors = await fetchAncestorNodesForTreeIndex(model, treeIndex, cogniteClient); + if (ancestors.length === 0) { + return null; + } - if (ancestors.length === 0) { - return null; - } + const mappings = await fetchNodeMappingEdges( + model, + ancestors.map((n) => n.id), + fdmClient + ); - const mappings = await fetchNodeMappingEdges(model, ancestors.map((n) => n.id), fdmClient); + const selectedEdge = + mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; - const selectedEdge = - mappings !== undefined && mappings.edges.length > 0 ? mappings.edges[0] : undefined; + const selectedNodeId = selectedEdge?.properties.revisionNodeId; - const selectedNodeId = selectedEdge?.properties.revisionNodeId; + const dataNode = selectedEdge?.startNode; - const dataNode = selectedEdge?.startNode; + if (dataNode === undefined) { + return null; + } - if (dataNode === undefined) { - return null; - } + const inspectionResult = await inspectNode(dataNode, fdmClient); - const inspectionResult = await inspectNode(dataNode, fdmClient); + const dataView = + inspectionResult?.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; - const dataView = - inspectionResult?.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; + if (dataView === undefined) { + return null; + } - if (dataView === undefined) { - return null; - } + const selectedNode = ancestors.find((n) => n.id === selectedNodeId); - const selectedNode = ancestors.find((n) => n.id === selectedNodeId)!; + assert(selectedNode !== undefined); - return { - nodeExternalId: dataNode.externalId, - view: dataView, - cadNode: selectedNode - }; - }); + return { + nodeExternalId: dataNode.externalId, + view: dataView, + cadNode: selectedNode + }; + }); return queryResult.data ?? undefined; }; @@ -79,9 +85,8 @@ export const useNodeMappedData = ( async function fetchAncestorNodesForTreeIndex( model: CogniteCadModel, treeIndex: number, - cogniteClient: CogniteClient, + cogniteClient: CogniteClient ): Promise { - const nodeId = await model.mapTreeIndexToNodeId(treeIndex); const ancestorNodes = await cogniteClient.revisions3D.list3DNodeAncestors( @@ -98,7 +103,6 @@ async function fetchNodeMappingEdges( ancestorIds: CogniteInternalId[], fdmClient: FdmSDK ): Promise<{ edges: Array>> } | undefined> { - assert(ancestorIds.length !== 0); const filter = { @@ -135,11 +139,13 @@ async function fetchNodeMappingEdges( ] }; - return fdmClient.filterAllInstances(filter, 'edge', SYSTEM_3D_EDGE_SOURCE); + return await fdmClient.filterAllInstances(filter, 'edge', SYSTEM_3D_EDGE_SOURCE); } -async function inspectNode(dataNode: DmsUniqueIdentifier, fdmClient: FdmSDK): Promise { - +async function inspectNode( + dataNode: DmsUniqueIdentifier, + fdmClient: FdmSDK +): Promise { const inspectionResult = await fdmClient.inspectInstances({ inspectionOperations: { involvedViewsAndContainers: {} }, items: [ diff --git a/react-components/stories/HighlightNode.stories.tsx b/react-components/stories/HighlightNode.stories.tsx index cd43645db6c..da9bf2508fa 100644 --- a/react-components/stories/HighlightNode.stories.tsx +++ b/react-components/stories/HighlightNode.stories.tsx @@ -10,7 +10,7 @@ import { type AddResourceOptions, CameraController, useClickedNodeData, - FdmAssetStylingGroup, + type FdmAssetStylingGroup } from '../src'; import { Color } from 'three'; import { type ReactElement, useState, useEffect, useRef } from 'react'; @@ -51,13 +51,13 @@ export const Main: Story = { @@ -87,11 +87,8 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React return ( <> - + - + ); }; From b3daabb9210c2e7176ecb1f99d88c5c40ab558bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 15 Aug 2023 16:21:46 +0200 Subject: [PATCH 21/23] fix: add zoom-to-clicked in HighlightNode --- .../src/components/Reveal3DResources/Reveal3DResources.tsx | 1 - react-components/stories/HighlightNode.stories.tsx | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx index a811c4315d2..05b70935eb4 100644 --- a/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx +++ b/react-components/src/components/Reveal3DResources/Reveal3DResources.tsx @@ -21,7 +21,6 @@ import { import { useCalculateModelsStyling } from '../../hooks/useCalculateModelsStyling'; import { useClickedNodeData } from '../..'; - export const Reveal3DResources = ({ resources, defaultResourceStyling, diff --git a/react-components/stories/HighlightNode.stories.tsx b/react-components/stories/HighlightNode.stories.tsx index 32a4e1a696d..5d14801b2bc 100644 --- a/react-components/stories/HighlightNode.stories.tsx +++ b/react-components/stories/HighlightNode.stories.tsx @@ -62,12 +62,15 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React const stylingGroupsRef = useRef([]); const nodeData = useClickedNodeData(); + const cameraNavigation = useCameraNavigation(); useEffect(() => { setHighlightedId(nodeData?.nodeExternalId); - }, [nodeData?.nodeExternalId]); - const cameraNavigation = useCameraNavigation(); + if (nodeData === undefined) return; + + cameraNavigation.fitCameraToInstance(nodeData.nodeExternalId, 'pdms-mapping'); + }, [nodeData?.nodeExternalId]); if (stylingGroupsRef.current.length === 1) { stylingGroupsRef.current.pop(); From 8893ab6188b0cf6e9482d9a5da8445747fb87211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Tue, 15 Aug 2023 16:27:30 +0200 Subject: [PATCH 22/23] chore: lint fix --- react-components/stories/HighlightNode.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react-components/stories/HighlightNode.stories.tsx b/react-components/stories/HighlightNode.stories.tsx index 5d14801b2bc..1f409501d73 100644 --- a/react-components/stories/HighlightNode.stories.tsx +++ b/react-components/stories/HighlightNode.stories.tsx @@ -69,7 +69,7 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React if (nodeData === undefined) return; - cameraNavigation.fitCameraToInstance(nodeData.nodeExternalId, 'pdms-mapping'); + void cameraNavigation.fitCameraToInstance(nodeData.nodeExternalId, 'pdms-mapping'); }, [nodeData?.nodeExternalId]); if (stylingGroupsRef.current.length === 1) { From 201fbb1525cb9a7a6ea5566e3130eaeff2e2c549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Flatval?= Date: Wed, 16 Aug 2023 10:03:00 +0200 Subject: [PATCH 23/23] chore: remove old `queryMappedData` file --- .../Reveal3DResources/queryMappedData.ts | 149 ------------------ 1 file changed, 149 deletions(-) delete mode 100644 react-components/src/components/Reveal3DResources/queryMappedData.ts diff --git a/react-components/src/components/Reveal3DResources/queryMappedData.ts b/react-components/src/components/Reveal3DResources/queryMappedData.ts deleted file mode 100644 index 4ccea77e925..00000000000 --- a/react-components/src/components/Reveal3DResources/queryMappedData.ts +++ /dev/null @@ -1,149 +0,0 @@ -/*! - * Copyright 2023 Cognite AS - */ - -import { type Cognite3DViewer, type PointerEventData, type CogniteCadModel } from '@cognite/reveal'; -import { type CogniteInternalId, type CogniteClient, type Node3D } from '@cognite/sdk'; -import { - type EdgeItem, - type InspectResultList, - type FdmSDK, - type DmsUniqueIdentifier -} from '../../utilities/FdmSDK'; -import { type NodeDataResult } from './types'; -import assert from 'assert'; -import { - INSTANCE_SPACE_3D_DATA, - type InModel3dEdgeProperties, - SYSTEM_3D_EDGE_SOURCE -} from '../../utilities/globalDataModels'; - -export async function queryMappedData( - viewer: Cognite3DViewer, - cdfClient: CogniteClient, - fdmClient: FdmSDK, - clickEvent: PointerEventData -): Promise { - const intersection = await viewer.getIntersectionFromPixel( - clickEvent.offsetX, - clickEvent.offsetY - ); - - if (intersection === null || intersection.type !== 'cad') { - return; - } - - const cadIntersection = intersection; - const model = cadIntersection.model; - - const ancestors = await getAncestorNodesForTreeIndex(cdfClient, model, cadIntersection.treeIndex); - - const mappings = await getMappingEdges( - fdmClient, - model, - ancestors.map((n) => n.id) - ); - - if (mappings.edges.length === 0) { - return; - } - - const selectedEdge = mappings.edges[0]; - const selectedNodeId = selectedEdge.properties.revisionNodeId; - const selectedNode = ancestors.find((n) => n.id === selectedNodeId); - assert(selectedNode !== undefined); - - const dataNode = selectedEdge.startNode; - - const inspectionResult = await inspectNode(fdmClient, dataNode); - - const dataView = - inspectionResult.items[0]?.inspectionResults.involvedViewsAndContainers?.views[0]; - - return { - nodeExternalId: dataNode.externalId, - view: dataView, - cadNode: selectedNode, - intersection: cadIntersection - }; -} - -async function getAncestorNodesForTreeIndex( - client: CogniteClient, - model: CogniteCadModel, - treeIndex: number -): Promise { - const nodeId = await model.mapTreeIndexToNodeId(treeIndex); - - const ancestorNodes = await client.revisions3D.list3DNodeAncestors( - model.modelId, - model.revisionId, - nodeId - ); - - return ancestorNodes.items; -} - -async function getMappingEdges( - fdmClient: FdmSDK, - model: CogniteCadModel, - ancestorIds: CogniteInternalId[] -): Promise<{ edges: Array> }> { - const filter = { - and: [ - { - equals: { - property: ['edge', 'endNode'], - value: { - space: INSTANCE_SPACE_3D_DATA, - externalId: `${model.modelId}` - } - } - }, - { - equals: { - property: [ - SYSTEM_3D_EDGE_SOURCE.space, - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, - 'revisionId' - ], - value: model.revisionId - } - }, - { - in: { - property: [ - SYSTEM_3D_EDGE_SOURCE.space, - `${SYSTEM_3D_EDGE_SOURCE.externalId}/${SYSTEM_3D_EDGE_SOURCE.version}`, - 'revisionNodeId' - ], - values: ancestorIds - } - } - ] - }; - - return await fdmClient.filterAllInstances( - filter, - 'edge', - SYSTEM_3D_EDGE_SOURCE - ); -} - -async function inspectNode( - fdmClient: FdmSDK, - dataNode: DmsUniqueIdentifier -): Promise { - const inspectionResult = await fdmClient.inspectInstances({ - inspectionOperations: { involvedViewsAndContainers: {} }, - items: [ - { - instanceType: 'node', - externalId: dataNode.externalId, - space: dataNode.space - } - ] - }); - - return inspectionResult; -}