diff --git a/react-components/package.json b/react-components/package.json index b9b087ebe9f..c1568fd8e62 100644 --- a/react-components/package.json +++ b/react-components/package.json @@ -1,6 +1,6 @@ { "name": "@cognite/reveal-react-components", - "version": "0.55.5", + "version": "0.55.6", "exports": { ".": { "import": "./dist/index.js", diff --git a/react-components/src/architecture/concrete/clipping/SliceDomainObject.ts b/react-components/src/architecture/concrete/clipping/SliceDomainObject.ts index 5560497a8d5..f8701aa82bc 100644 --- a/react-components/src/architecture/concrete/clipping/SliceDomainObject.ts +++ b/react-components/src/architecture/concrete/clipping/SliceDomainObject.ts @@ -21,8 +21,8 @@ export class SliceDomainObject extends PlaneDomainObject { public constructor(primitiveType: PrimitiveType) { super(primitiveType); - this.color = new Color(Color.NAMES.greenyellow); - this.backSideColor = new Color(Color.NAMES.red); + this.color = new Color(Color.NAMES.orangered); + this.backSideColor = new Color(Color.NAMES.palegreen); } // ================================================== diff --git a/react-components/src/architecture/concrete/primitives/line/LineView.ts b/react-components/src/architecture/concrete/primitives/line/LineView.ts index 4ba5677ab6e..830b67d17c0 100644 --- a/react-components/src/architecture/concrete/primitives/line/LineView.ts +++ b/react-components/src/architecture/concrete/primitives/line/LineView.ts @@ -94,7 +94,7 @@ export class LineView extends GroupThreeView { return undefined; // Should never be picked } // Implement the intersection logic here, because of bug in tree.js - const radius = getRadius(domainObject, style); + const radius = getSelectRadius(domainObject, style); if (radius <= 0) { return; } @@ -108,7 +108,7 @@ export class LineView extends GroupThreeView { const thisPoint = new Vector3(); const intersection = new Vector3(); - const radiusSquared = square(1.5 * radius); // Add 50% more to make it easier to pick + const radiusSquared = square(radius); const ray = intersectInput.raycaster.ray; const closestFinder = new ClosestGeometryFinder(ray.origin); @@ -341,3 +341,7 @@ function adjustLabel( function getRadius(domainObject: LineDomainObject, style: LineRenderStyle): number { return domainObject.isSelected ? style.selectedPipeRadius : style.pipeRadius; } + +function getSelectRadius(domainObject: LineDomainObject, style: LineRenderStyle): number { + return 1.5 * style.selectedPipeRadius; // Added more to make it easier to pick +} diff --git a/react-components/src/architecture/concrete/primitives/plane/PlaneCreator.ts b/react-components/src/architecture/concrete/primitives/plane/PlaneCreator.ts index 515f224e886..39fb0b34f42 100644 --- a/react-components/src/architecture/concrete/primitives/plane/PlaneCreator.ts +++ b/react-components/src/architecture/concrete/primitives/plane/PlaneCreator.ts @@ -89,29 +89,36 @@ export class PlaneCreator extends BaseCreator { throw new Error('Cannot create a plane without points'); } const domainObject = this._domainObject; + let normal: Vector3; switch (domainObject.primitiveType) { case PrimitiveType.PlaneX: - domainObject.plane.setFromNormalAndCoplanarPoint(new Vector3(1, 0, 0), this.firstPoint); + normal = new Vector3(1, 0, 0); break; case PrimitiveType.PlaneY: - domainObject.plane.setFromNormalAndCoplanarPoint(new Vector3(0, 1, 0), this.firstPoint); + normal = new Vector3(0, 1, 0); break; case PrimitiveType.PlaneZ: - domainObject.plane.setFromNormalAndCoplanarPoint(new Vector3(0, 0, 1), this.firstPoint); + normal = new Vector3(0, 0, 1); break; case PrimitiveType.PlaneXY: if (this.pointCount === 1) { - const normal = ray.direction.clone().normalize(); - domainObject.plane.setFromNormalAndCoplanarPoint(normal, this.firstPoint); - } else if (this.pointCount === 2) { - const normal = new Vector3().subVectors(this.lastPoint, this.firstPoint).normalize(); + normal = ray.direction.clone().normalize(); + } else { + normal = new Vector3().subVectors(this.lastPoint, this.firstPoint).normalize(); rotatePiHalf(normal); - domainObject.plane.setFromNormalAndCoplanarPoint(normal, this.firstPoint); } break; + + default: + return; + } + // Make sure that the normal is pointing towards the camera + if (ray.direction.dot(normal) < 0) { + normal.negate(); } + domainObject.plane.setFromNormalAndCoplanarPoint(normal, this.firstPoint); } } diff --git a/react-components/src/architecture/concrete/primitives/plane/PlaneRenderStyle.ts b/react-components/src/architecture/concrete/primitives/plane/PlaneRenderStyle.ts index 0be0ff11cdc..baecfa626e9 100644 --- a/react-components/src/architecture/concrete/primitives/plane/PlaneRenderStyle.ts +++ b/react-components/src/architecture/concrete/primitives/plane/PlaneRenderStyle.ts @@ -17,8 +17,8 @@ export class PlaneRenderStyle extends CommonRenderStyle { public linesColor = BLACK_COLOR.clone(); public selectedLinesColor = WHITE_COLOR.clone(); public opacityUse = true; - public opacity = 0.5; - public selectedOpacity = 0.8; + public opacity = 0.25; + public selectedOpacity = 0.5; // ================================================== // OVERRIDES of BaseStyle diff --git a/react-components/src/components/CacheProvider/AnnotationModelUtils.ts b/react-components/src/components/CacheProvider/AnnotationModelUtils.ts index e6c164b3461..cb92a0e63dc 100644 --- a/react-components/src/components/CacheProvider/AnnotationModelUtils.ts +++ b/react-components/src/components/CacheProvider/AnnotationModelUtils.ts @@ -23,7 +23,10 @@ export async function fetchPointCloudAnnotationAssets( }); const filteredAnnotationMapping = annotationMapping.filter(isDefined); - const uniqueAnnotationMapping = uniqBy(filteredAnnotationMapping, 'assetId'); + const uniqueAnnotationMapping = uniqBy( + filteredAnnotationMapping, + (annotationMapping) => annotationMapping.assetId + ); const assetIds = uniqueAnnotationMapping.map((mapping) => mapping.assetId); const assets = await fetchAssetForAssetIds(assetIds, sdk); diff --git a/react-components/src/components/RuleBasedOutputs/hooks/useExtractUniqueAssetIdsFromMapped.tsx b/react-components/src/components/RuleBasedOutputs/hooks/useExtractUniqueAssetIdsFromMapped.tsx index 60c65908077..e9f9f6ed1f3 100644 --- a/react-components/src/components/RuleBasedOutputs/hooks/useExtractUniqueAssetIdsFromMapped.tsx +++ b/react-components/src/components/RuleBasedOutputs/hooks/useExtractUniqueAssetIdsFromMapped.tsx @@ -17,7 +17,7 @@ export const useExtractUniqueAssetIdsFromMapped = ( id: item.assetId }; }); - const uniqueAssetIds = uniqBy(assetIds, 'id'); + const uniqueAssetIds = uniqBy(assetIds, (assetId) => assetId.id); return uniqueAssetIds; }, [assetMappings]); }; diff --git a/react-components/src/index.ts b/react-components/src/index.ts index 986ad954ca5..5165d53b1b9 100644 --- a/react-components/src/index.ts +++ b/react-components/src/index.ts @@ -114,7 +114,8 @@ export { type AddImage360CollectionOptions, type AddResourceOptions, type AddCadResourceOptions, - type AddPointCloudResourceOptions + type AddPointCloudResourceOptions, + type CadModelOptions } from './components/Reveal3DResources/types'; export { type PointCloudAnnotationMappedAssetData, diff --git a/react-components/src/query/useSearchAssetsMapped360Annotations.tsx b/react-components/src/query/useSearchAssetsMapped360Annotations.tsx index ffb308434f5..827ce111b4e 100644 --- a/react-components/src/query/useSearchAssetsMapped360Annotations.tsx +++ b/react-components/src/query/useSearchAssetsMapped360Annotations.tsx @@ -116,7 +116,10 @@ async function get360AnnotationAssets( }) .filter(isDefined); - const uniqueAnnotationMapping = uniqBy(filteredAnnotationMappings, 'assetId'); + const uniqueAnnotationMapping = uniqBy( + filteredAnnotationMappings, + (annotationMapping) => annotationMapping.assetId + ); const assets = await retrieveAssets(sdk, uniqueAnnotationMapping); const flatAssets = assets.flat(); diff --git a/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx b/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx index 374b50a67ff..34244a5159a 100644 --- a/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx +++ b/react-components/src/query/useSearchMappedEquipmentAssetMappings.tsx @@ -1,7 +1,6 @@ /*! * Copyright 2023 Cognite AS */ -import { useRef } from 'react'; import { type AddModelOptions } from '@cognite/reveal'; import { type Asset, @@ -18,7 +17,7 @@ import { useSDK } from '../components/RevealCanvas/SDKProvider'; import { getAssetsList } from '../hooks/network/getAssetsList'; import { useAssetMappedNodesForRevisions } from '../components/CacheProvider/AssetMappingAndNode3DCacheProvider'; import { isDefined } from '../utilities/isDefined'; -import { uniq } from 'lodash'; +import { uniqBy } from 'lodash'; export type ModelMappings = { model: AddModelOptions; @@ -48,8 +47,6 @@ export const useSearchMappedEquipmentAssetMappings = ( const sdk = useSDK(userSdk); const { data: assetMappingList, isFetched: isAssetMappingNodesFetched } = useAssetMappedNodesForRevisions(models.map((model) => ({ ...model, type: 'cad' }))); - const { data: initialAssetMappings, isLoading: isInitialAssetMappingsLoading } = - useAllMappedEquipmentAssetMappings(models, sdk); return useInfiniteQuery({ queryKey: [ @@ -60,38 +57,47 @@ export const useSearchMappedEquipmentAssetMappings = ( ...models.map((model) => [model.modelId, model.revisionId]) ], queryFn: async ({ pageParam }: { pageParam: string | undefined }) => { - if (initialAssetMappings === undefined) { + if (query === '' || assetMappingList === undefined) { return { assets: [], nextCursor: undefined }; } - if (query === '') { - const assets = initialAssetMappings.pages.flatMap((modelWithAssets) => - modelWithAssets.modelsAssets.flatMap((modelsAsset) => modelsAsset.assets).flat() + + const fetchAssets = async ( + cursor: string | undefined, + accumulatedAssets: Asset[] + ): Promise<{ assets: Asset[]; nextCursor: string | undefined }> => { + const assetsResponse = await getAssetsList(sdk, { + query, + limit, + cursor + }); + + const fetchedAssets = assetsResponse.items.filter(isDefined); + const filteredSearchedAssets = assetMappingList.flatMap((mapping) => { + return mapping.assetMappings + .filter((assetMapping) => + fetchedAssets.some((asset) => asset.id === assetMapping.assetId) + ) + .map((assetMapping) => fetchedAssets.find((asset) => asset.id === assetMapping.assetId)) + .filter(isDefined); + }); + + const uniqueAssets = uniqBy( + [...accumulatedAssets, ...filteredSearchedAssets], + (asset) => asset.id ); - return { assets, nextCursor: undefined }; - } - if (assetMappingList === undefined) { - return { assets: [], nextCursor: undefined }; - } - const assetsResponse = await getAssetsList(sdk, { - query, - limit, - cursor: pageParam - }); - const assets = assetsResponse.items.filter(isDefined); - const filteredSearchedAssets = assetMappingList.flatMap((mapping) => { - return mapping.assetMappings - .filter((assetMapping) => assets.some((asset) => asset.id === assetMapping.assetId)) - .map((assetMapping) => assets.find((asset) => asset.id === assetMapping.assetId)) - .filter(isDefined); - }); + if (uniqueAssets.length >= limit || assetsResponse.nextCursor === undefined) { + return { assets: uniqueAssets, nextCursor: assetsResponse.nextCursor }; + } - // Remove duplicates - const uniqueFilteredSearchedAssets = uniq(filteredSearchedAssets); + return await fetchAssets(assetsResponse.nextCursor, uniqueAssets); + }; + + const { assets, nextCursor } = await fetchAssets(pageParam, []); return { - assets: uniqueFilteredSearchedAssets, - nextCursor: assetsResponse.nextCursor + assets, + nextCursor }; }, initialPageParam: undefined, @@ -101,10 +107,7 @@ export const useSearchMappedEquipmentAssetMappings = ( return lastPageData.nextCursor; }, enabled: - !isInitialAssetMappingsLoading && - isAssetMappingNodesFetched && - assetMappingList !== undefined && - assetMappingList.length > 0 + isAssetMappingNodesFetched && assetMappingList !== undefined && assetMappingList.length > 0 }); }; @@ -112,9 +115,8 @@ export const useAllMappedEquipmentAssetMappings = ( models: AddModelOptions[], userSdk?: CogniteClient, limit: number = 1000 -): UseInfiniteQueryResult, Error> => { +): UseInfiniteQueryResult, Error> => { const sdk = useSDK(userSdk); - const usedCursors = useRef(new Set()); return useInfiniteQuery({ queryKey: [ @@ -129,13 +131,11 @@ export const useAllMappedEquipmentAssetMappings = ( cursor: string | 'start' | undefined; model: AddModelOptions; }>; - const nextCursor = nextCursors.find( (nextCursor) => nextCursor.model.modelId === model.modelId && nextCursor.model.revisionId === model.revisionId )?.cursor; - if (nextCursor === undefined) { return { mappings: { items: [] }, model }; } @@ -145,43 +145,18 @@ export const useAllMappedEquipmentAssetMappings = ( limit }); - usedCursors.current.add(nextCursor); - return { mappings, model }; }); const currentPagesOfAssetMappings = await Promise.all(currentPagesOfAssetMappingsPromises); const modelsAssets = await getAssetsFromAssetMappings(sdk, currentPagesOfAssetMappings); - const nextCursors = currentPagesOfAssetMappings - .map(({ mappings }) => mappings.nextCursor) - .filter(isDefined); - return await Promise.resolve({ - modelsAssets, - nextCursors - }); + return modelsAssets; }, initialPageParam: models.map((model) => ({ cursor: 'start', model })), staleTime: Infinity, - getNextPageParam: (lastPage: { - modelsAssets: ModelMappingsWithAssets[]; - nextCursors: string[]; - }): Array<{ cursor: string | undefined; model: AddModelOptions }> | undefined => { - const nextCursors = lastPage.nextCursors - .map((cursor, index) => ({ cursor, model: lastPage.modelsAssets[index].model })) - .filter((mappingModel) => { - if (mappingModel.cursor === undefined || usedCursors.current.has(mappingModel.cursor)) { - return false; - } - usedCursors.current.add(mappingModel.cursor); - return true; - }); - if (nextCursors.length === 0) { - return undefined; - } - return nextCursors; - } + getNextPageParam }); }; diff --git a/react-components/src/utilities/buildFilter.ts b/react-components/src/utilities/buildFilter.ts index 7bd56fb27a3..540af4edc06 100644 --- a/react-components/src/utilities/buildFilter.ts +++ b/react-components/src/utilities/buildFilter.ts @@ -6,27 +6,16 @@ export const buildFilter = (query: string): any => { if (query === '') { return undefined; } - const conditions = ['search'] - .map((condition) => [ - { - [condition]: { - property: ['name'], - value: query - } - }, - { - [condition]: { - property: ['description'], - value: query - } - } - ]) - .flat(); + + const searchConditions = [ + { search: { property: ['name'], value: query } }, + { search: { property: ['description'], value: query } } + ]; return { and: [ { - or: conditions + or: searchConditions } ] }; diff --git a/react-components/stories/SearchHooks.stories.tsx b/react-components/stories/SearchHooks.stories.tsx index 85eb468b938..05f33c32332 100644 --- a/react-components/stories/SearchHooks.stories.tsx +++ b/react-components/stories/SearchHooks.stories.tsx @@ -126,7 +126,15 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React } else if (searchMethod === 'assetSearch' && !isAssetSearchFetching && assetSearchHasNextPage) { void fetchAssetSearchNextPage(); } - }, []); + }, [ + searchMethod, + isFetching, + hasNextPage, + fetchNextPage, + isAssetSearchFetching, + assetSearchHasNextPage, + fetchAssetSearchNextPage + ]); const filteredEquipment = useMemo(() => { if (searchMethod === 'allFdm') { @@ -152,9 +160,7 @@ const StoryContent = ({ resources }: { resources: AddResourceOptions[] }): React const transformedAssets = allAssets?.pages .flat() - .map((modelsAssetPage) => - modelsAssetPage.modelsAssets.flatMap((modelsAsset) => modelsAsset.assets) - ) + .map((mapping) => mapping.assets) .flat() ?? []; const all360ImageAssets = @@ -372,8 +378,8 @@ export const Main: Story = { siteId: 'celanese1' }, { - modelId: 5653798104332258, - revisionId: 5045518244111296 + modelId: 7646043527629245, + revisionId: 6059566106376463 } ] },