Skip to content

Commit

Permalink
Merge master into pramodcog/bump-react-recomponent-v0.6.1
Browse files Browse the repository at this point in the history
  • Loading branch information
cognite-bulldozer[bot] authored Aug 4, 2023
2 parents 46fd42f + 81e6f6c commit 7968126
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ export function PointCloudContainer({
async function addModel(modelId: number, revisionId: number, transform?: Matrix4): Promise<void> {
const pointCloudModel = await viewer.addPointCloudModel({ modelId, revisionId });

viewer.fitCameraToModel(pointCloudModel);

if (transform !== undefined) {
pointCloudModel.setModelTransformation(transform);
}
Expand Down
29 changes: 19 additions & 10 deletions react-components/src/components/ViewerAnchor/ViewerAnchor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright 2023 Cognite AS
*/

import { useEffect, useRef, type ReactElement, type RefObject, useState } from 'react';
import { useEffect, useRef, type ReactElement, type RefObject, useState, useCallback } from 'react';
import { Vector2, type Vector3 } from 'three';

import { useReveal } from '../RevealContainer/RevealContext';
Expand All @@ -22,20 +22,29 @@ export const ViewerAnchor = ({ position, children }: ViewerAnchorProps): ReactEl
const [divTranslation, setDivTranslation] = useState(new Vector2());
const [visible, setVisible] = useState(false);

const cameraChanged = (cameraPosition: Vector3, cameraTarget: Vector3): void => {
const cameraDirection = cameraTarget.clone().sub(cameraPosition).normalize();
const elementDirection = position.clone().sub(cameraPosition).normalize();
const cameraChanged = useCallback(
(cameraPosition: Vector3, cameraTarget: Vector3): void => {
const cameraDirection = cameraTarget.clone().sub(cameraPosition).normalize();
const elementDirection = position.clone().sub(cameraPosition).normalize();

setVisible(elementDirection.dot(cameraDirection) > 0);
setVisible(elementDirection.dot(cameraDirection) > 0);

const screenSpacePosition = viewer.worldToScreen(position.clone());
if (screenSpacePosition !== null) {
setDivTranslation(screenSpacePosition);
}
};
const screenSpacePosition = viewer.worldToScreen(position.clone());
if (screenSpacePosition !== null) {
setDivTranslation(screenSpacePosition);
}
},
[viewer, position]
);

useEffect(() => {
viewer.cameraManager.on('cameraChange', cameraChanged);

cameraChanged(
viewer.cameraManager.getCameraState().position,
viewer.cameraManager.getCameraState().target
);

return () => {
viewer.cameraManager.off('cameraChange', cameraChanged);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,11 @@ export function withSuppressRevealEvents<T extends JSX.IntrinsicAttributes>(
}

div.addEventListener('pointerdown', stopPropagation);
div.addEventListener('pointerup', stopPropagation);
div.addEventListener('pointermove', stopPropagation);
div.addEventListener('wheel', stopPropagation);

return () => {
div.removeEventListener('pointerdown', stopPropagation);
div.removeEventListener('pointerup', stopPropagation);
div.removeEventListener('pointermove', stopPropagation);
div.removeEventListener('wheel', stopPropagation);
};
Expand Down
8 changes: 7 additions & 1 deletion react-components/src/hooks/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*!
* Copyright 2023 Cognite AS
*/
import { type CogniteExternalId, type CogniteInternalId } from '@cognite/sdk';
import { type Source } from '../utilities/FdmSDK';

export type FdmAssetMappingsConfig = {
Expand All @@ -22,5 +23,10 @@ export type FdmAssetMappingsConfig = {
export type ThreeDModelMappings = {
modelId: number;
revisionId: number;
mappings: Array<{ nodeId: number; externalId: string }>;
mappings: Map<CogniteExternalId, CogniteInternalId>;
};

export type Model3DEdgeProperties = {
revisionId: number;
revisionNodeId: number;
};
119 changes: 79 additions & 40 deletions react-components/src/hooks/useCalculateModelsStyling.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
/*!
* Copyright 2023 Cognite AS
*/
import { useMemo } from 'react';
import { type FdmAssetMappingsConfig } from './types';
import { useEffect, useMemo } from 'react';
import { type ThreeDModelMappings, type FdmAssetMappingsConfig } from './types';
import { type Reveal3DResourcesStyling } from '../components/Reveal3DResources/Reveal3DResources';
import { type TypedReveal3DModel } from '../components/Reveal3DResources/types';
import { useFdmAssetMappings } from './useFdmAssetMappings';
import { type PointCloudModelStyling } from '../components/PointCloudContainer/PointCloudContainer';
import {
type CadModelStyling,
type NodeStylingGroup
} from '../components/CadModelContainer/CadModelContainer';
import { type CadModelStyling } from '../components/CadModelContainer/CadModelContainer';
import { type CogniteExternalId, type CogniteInternalId } from '@cognite/sdk';

/**
* Calculates the styling for the models based on the styling configuration and the mappings.
Expand All @@ -29,49 +27,30 @@ export const useCalculateModelsStyling = (
[styling]
);

const { data: mappings } = useFdmAssetMappings(stylingExternalIds, fdmAssetMappingConfig);
const {
data: mappings,
hasNextPage,
fetchNextPage,
isFetchingNextPage
} = useFdmAssetMappings(stylingExternalIds, fdmAssetMappingConfig);

useEffect(() => {
if (hasNextPage !== undefined && !isFetchingNextPage) {
void fetchNextPage();
}
}, [hasNextPage, fetchNextPage]);

const modelsStyling = useMemo(() => {
if (styling === undefined || models === undefined) return [];

const allPagesMappings = mappings?.pages.flatMap((page) => page.items);

const internalModelsStyling = models.map((model) => {
let modelStyling: PointCloudModelStyling | CadModelStyling;

switch (model.type) {
case 'cad': {
const modelNodeMappings = mappings?.find(
(mapping) =>
mapping.modelId === model.modelId && mapping.revisionId === model.revisionId
);

const newStylingGroups: NodeStylingGroup[] | undefined =
styling.groups !== null ? [] : undefined;

styling.groups?.forEach((group) => {
const connectedExternalIds = group.fdmAssetExternalIds.filter(
(externalId) =>
modelNodeMappings?.mappings.some(
(modelNodeMapping) => modelNodeMapping.externalId === externalId
)
);

const newGroup: NodeStylingGroup = {
style: group.style.cad,
nodeIds: connectedExternalIds.map((externalId) => {
const mapping = modelNodeMappings?.mappings.find(
(mapping) => mapping.externalId === externalId
);
return mapping?.nodeId ?? -1;
})
};

if (connectedExternalIds.length > 0) newStylingGroups?.push(newGroup);
});

modelStyling = {
defaultStyle: styling.defaultStyle?.cad,
groups: newStylingGroups
};
modelStyling = calculateCadModelStyling(styling, allPagesMappings, model);
break;
}
case 'pointcloud': {
Expand All @@ -94,3 +73,63 @@ export const useCalculateModelsStyling = (

return modelsStyling;
};

function getModelMappings(
mappings: ThreeDModelMappings[] | undefined,
model: TypedReveal3DModel
): Map<CogniteExternalId, CogniteInternalId> | undefined {
return mappings
?.filter(
(mapping) => mapping.modelId === model.modelId && mapping.revisionId === model.revisionId
)
.reduce(
(acc, mapping) => {
// reduce is added to avoid duplicate models from several pages.
mergeMaps(acc.mappings, mapping.mappings);
return acc;
},
{ modelId: model.modelId, revisionId: model.revisionId, mappings: new Map<string, number>() }
).mappings;
}

function calculateCadModelStyling(
styling: Reveal3DResourcesStyling,
mappings: ThreeDModelMappings[] | undefined,
model: TypedReveal3DModel
): CadModelStyling {
const modelMappings = getModelMappings(mappings, model);

const resourcesStylingGroups = styling?.groups;

if (resourcesStylingGroups === undefined || modelMappings === undefined)
return {
defaultStyle: styling.defaultStyle?.cad
};

const modelStylingGroups = resourcesStylingGroups
.map((resourcesGroup) => {
const modelMappedNodeIds = resourcesGroup.fdmAssetExternalIds
.map((externalId) => modelMappings.get(externalId))
.filter((nodeId): nodeId is number => nodeId !== undefined);

return {
style: resourcesGroup.style.cad,
nodeIds: modelMappedNodeIds
};
})
.filter((group) => group.nodeIds.length > 0);

return {
defaultStyle: styling.defaultStyle?.cad,
groups: modelStylingGroups
};
}

function mergeMaps(
targetMap: Map<string, number>,
addedMap: Map<string, number>
): Map<string, number> {
addedMap.forEach((value, key) => targetMap.set(key, value));

return targetMap;
}
45 changes: 25 additions & 20 deletions react-components/src/hooks/useFdmAssetMappings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
*/
import { type CogniteExternalId } from '@cognite/sdk';
import { useFdmSdk } from '../components/RevealContainer/SDKProvider';
import { type UseQueryResult, useQuery } from '@tanstack/react-query';
import { type FdmAssetMappingsConfig, type ThreeDModelMappings } from './types';
import { useInfiniteQuery, type UseInfiniteQueryResult } from '@tanstack/react-query';
import {
type Model3DEdgeProperties,
type FdmAssetMappingsConfig,
type ThreeDModelMappings
} from './types';
import { DEFAULT_QUERY_STALE_TIME } from '../utilities/constants';

/**
Expand All @@ -13,13 +17,13 @@ import { DEFAULT_QUERY_STALE_TIME } from '../utilities/constants';
export const useFdmAssetMappings = (
fdmAssetExternalIds: CogniteExternalId[],
fdmConfig?: FdmAssetMappingsConfig
): UseQueryResult<ThreeDModelMappings[]> => {
): UseInfiniteQueryResult<{ items: ThreeDModelMappings[]; nextCursor: string }> => {
const fdmSdk = useFdmSdk();

return useQuery(
return useInfiniteQuery(
['reveal', 'react-components', fdmAssetExternalIds],
async () => {
if (fdmAssetExternalIds?.length === 0) return [];
async ({ pageParam }) => {
if (fdmAssetExternalIds?.length === 0) return { items: [], nextCursor: undefined };
if (fdmConfig === undefined)
throw Error('FDM config must be defined when using FDM asset mappings');

Expand All @@ -36,16 +40,16 @@ export const useFdmAssetMappings = (
const instances = await fdmSdk.filterInstances(
fdmAssetMappingFilter,
'edge',
fdmConfig.source
fdmConfig.source,
pageParam
);

const modelMappingsTemp: ThreeDModelMappings[] = [];

instances.edges.forEach((instance) => {
const mappingProperty =
instance.properties[fdmConfig.source.space][
`${fdmConfig.source.externalId}/${fdmConfig.source.version}`
];
const mappingProperty = instance.properties[fdmConfig.source.space][
`${fdmConfig.source.externalId}/${fdmConfig.source.version}`
] as Model3DEdgeProperties;

const modelId = Number.parseInt(instance.endNode.externalId.slice(9));
const revisionId = mappingProperty.revisionId;
Expand All @@ -55,27 +59,28 @@ export const useFdmAssetMappings = (
);

if (!isAdded) {
const mappingsMap = new Map<string, number>();
mappingsMap.set(instance.startNode.externalId, mappingProperty.revisionNodeId);

modelMappingsTemp.push({
modelId,
revisionId,
mappings: [
{ nodeId: mappingProperty.revisionNodeId, externalId: instance.startNode.externalId }
]
mappings: mappingsMap
});
} else {
const modelMapping = modelMappingsTemp.find(
(mapping) => mapping.modelId === modelId && mapping.revisionId === revisionId
);

modelMapping?.mappings.push({
nodeId: mappingProperty.revisionNodeId,
externalId: instance.startNode.externalId
});
modelMapping?.mappings.set(instance.startNode.externalId, mappingProperty.revisionNodeId);
}
});

return modelMappingsTemp;
return { items: modelMappingsTemp, nextCursor: instances.nextCursor };
},
{ staleTime: DEFAULT_QUERY_STALE_TIME }
{
staleTime: DEFAULT_QUERY_STALE_TIME,
getNextPageParam: (lastPage) => lastPage.nextCursor
}
);
};
37 changes: 37 additions & 0 deletions react-components/src/hooks/useMappedEquipmentBy3DModelsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*!
* Copyright 2023 Cognite AS
*/
import { type UseQueryResult, useQuery } from '@tanstack/react-query';
import { type FdmAssetMappingsConfig } from '..';
import { useFdmSdk } from '../components/RevealContainer/SDKProvider';

export const useMappedEquipmentBy3DModelsList = (
Default3DFdmConfig: FdmAssetMappingsConfig,
modelsList: Array<{ modelId: number; revisionId: number }> = []
): UseQueryResult<string[]> => {
const fdmClient = useFdmSdk();

return useQuery(
['reveal', 'react-components', ...modelsList.map(({ modelId }) => modelId.toString()).sort()],
async () => {
const filter = {
in: {
property: ['edge', 'endNode'],
values: modelsList.map(({ modelId }) => ({
space: Default3DFdmConfig.global3dSpace,
externalId: `model_3d_${modelId}`
}))
}
};

const mappings = await fdmClient.filterAllInstances(
filter,
'edge',
Default3DFdmConfig.source
);

return mappings.edges.map((edge) => edge.startNode.externalId);
},
{ staleTime: Infinity }
);
};
12 changes: 1 addition & 11 deletions react-components/stories/HighlightNode.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,7 @@ import { Color, Matrix4 } from 'three';
import { type ReactElement, useState } from 'react';
import { DefaultNodeAppearance, TreeIndexNodeCollection } from '@cognite/reveal';
import { createSdkByUrlToken } from './utilities/createSdkByUrlToken';

const DefaultFdmConfig: FdmAssetMappingsConfig = {
source: {
space: 'hf_3d_schema',
version: '1',
type: 'view',
externalId: 'cdf_3d_connection_data'
},
global3dSpace: 'hf_3d_global_data',
assetFdmSpace: 'hf_customer_a'
};
import { DefaultFdmConfig } from './utilities/fdmConfig';

const meta = {
title: 'Example/HighlightNode',
Expand Down
Loading

0 comments on commit 7968126

Please sign in to comment.