diff --git a/viewer/package.json b/viewer/package.json index a6181588abf..3fa9263cd6b 100644 --- a/viewer/package.json +++ b/viewer/package.json @@ -1,6 +1,6 @@ { "name": "@cognite/reveal", - "version": "4.20.1", + "version": "4.21.0", "description": "WebGL based 3D viewer for CAD and point clouds processed in Cognite Data Fusion.", "homepage": "https://github.com/cognitedata/reveal/tree/master/viewer", "repository": { diff --git a/viewer/packages/360-images/src/Image360Facade.ts b/viewer/packages/360-images/src/Image360Facade.ts index 48582d00dc5..4c3430f7245 100644 --- a/viewer/packages/360-images/src/Image360Facade.ts +++ b/viewer/packages/360-images/src/Image360Facade.ts @@ -14,6 +14,7 @@ import { Image360RevisionEntity } from './entity/Image360RevisionEntity'; import { Image360AnnotationFilterOptions } from './annotation/types'; import { AsyncSequencer } from '@reveal/utilities/src/AsyncSequencer'; import { DataSourceType } from '@reveal/data-providers'; +import { Image360IconIntersectionData } from './types'; export class Image360Facade { private readonly _image360Collections: DefaultImage360Collection[]; @@ -121,7 +122,7 @@ export class Image360Facade { return imageCollection[0]; } - public intersect(coords: THREE.Vector2, camera: THREE.Camera): Image360Entity | undefined { + public intersect(coords: THREE.Vector2, camera: THREE.Camera): Image360IconIntersectionData | undefined { const cameraDirection = camera.getWorldDirection(new THREE.Vector3()); const cameraPosition = camera.position.clone(); const collectionMatrix = new THREE.Matrix4(); @@ -143,7 +144,9 @@ export class Image360Facade { .map(intersectionToCameraSpace) .filter(isInFrontOfCamera) .sort(byDistanceToCamera) - .map(selectEntity) + .map(([entity, intersectionPoint]) => + createIntersection(collection, entity, intersectionPoint, camera.position) + ) ); return first(intersections); @@ -207,8 +210,18 @@ export class Image360Facade { return a.lengthSq() - b.lengthSq(); } - function selectEntity([entity, _]: [Image360Entity, THREE.Vector3]): Image360Entity { - return entity; + function createIntersection( + image360Collection: DefaultImage360Collection, + image360: Image360Entity, + intersectionPoint: THREE.Vector3, + cameraPosition: THREE.Vector3 + ): Image360IconIntersectionData { + return { + image360, + image360Collection, + point: intersectionPoint, + distanceToCamera: intersectionPoint.distanceTo(cameraPosition) + }; } } diff --git a/viewer/packages/360-images/src/types.ts b/viewer/packages/360-images/src/types.ts index 040117cb95c..c4858c8164e 100644 --- a/viewer/packages/360-images/src/types.ts +++ b/viewer/packages/360-images/src/types.ts @@ -2,8 +2,12 @@ * Copyright 2023 Cognite AS */ +import { Vector3 } from 'three'; import { Image360 } from './entity/Image360'; import { Image360Revision } from './entity/Image360Revision'; +import { DataSourceType } from '@reveal/data-providers'; +import { Image360Entity } from './entity/Image360Entity'; +import { DefaultImage360Collection } from './collection/DefaultImage360Collection'; /** * Delegate for 360 image mode entered events. @@ -14,3 +18,10 @@ export type Image360EnteredDelegate = (image360: Image360, revision: Image360Rev * Delegate for 360 image mode exited events. */ export type Image360ExitedDelegate = () => void; + +export type Image360IconIntersectionData = { + image360Collection: DefaultImage360Collection; + image360: Image360Entity; + point: Vector3; + distanceToCamera: number; +}; diff --git a/viewer/packages/360-images/visual-tests/Image360.VisualTest.ts b/viewer/packages/360-images/visual-tests/Image360.VisualTest.ts index 9984ebf7bbf..48a4c4a4197 100644 --- a/viewer/packages/360-images/visual-tests/Image360.VisualTest.ts +++ b/viewer/packages/360-images/visual-tests/Image360.VisualTest.ts @@ -107,13 +107,15 @@ export default class Image360VisualTestFixture extends StreamingVisualTestFixtur renderer.domElement.addEventListener('click', async event => { const { x, y } = event; const ndcCoordinates = getNormalizedPixelCoordinates(renderer.domElement, x, y); - const entity = facade.intersect(new THREE.Vector2(ndcCoordinates.x, ndcCoordinates.y), camera); + const intersection = facade.intersect(new THREE.Vector2(ndcCoordinates.x, ndcCoordinates.y), camera); - if (entity === undefined) { + if (intersection === undefined) { this.render(); return; } + const entity = intersection.image360; + await facade.preload(entity, entity.getActiveRevision()); entity.image360Visualization.visible = true; entity.icon.setVisible(false); @@ -193,11 +195,12 @@ export default class Image360VisualTestFixture extends StreamingVisualTestFixtur renderer.domElement.addEventListener('mousemove', async event => { const { x, y } = event; const ndcCoordinates = getNormalizedPixelCoordinates(renderer.domElement, x, y); - const entity = facade.intersect(new THREE.Vector2(ndcCoordinates.x, ndcCoordinates.y), camera); - if (entity === undefined) { + const intersection = facade.intersect(new THREE.Vector2(ndcCoordinates.x, ndcCoordinates.y), camera); + if (intersection === undefined) { this.render(); return; } + const entity = intersection.image360; entity.icon.selected = true; await facade.preload(entity, entity.getActiveRevision()); entity.image360Visualization.visible = false; diff --git a/viewer/packages/api/src/api-helpers/Image360ApiHelper.ts b/viewer/packages/api/src/api-helpers/Image360ApiHelper.ts index a478a03e522..79dcf0f83ea 100644 --- a/viewer/packages/api/src/api-helpers/Image360ApiHelper.ts +++ b/viewer/packages/api/src/api-helpers/Image360ApiHelper.ts @@ -46,6 +46,7 @@ import { Image360WithCollection } from '../public/types'; import { DEFAULT_IMAGE_360_OPACITY } from '@reveal/360-images/src/entity/Image360VisualizationBox'; import { Image360History } from '@reveal/360-images/src/Image360History'; import { Image360Action } from '@reveal/360-images/src/Image360Action'; +import { Image360IconIntersectionData } from '@reveal/360-images/src/types'; export class Image360ApiHelper { private readonly _image360Facade: Image360Facade; @@ -568,20 +569,22 @@ export class Image360ApiHelper { if (this._transitionInProgress) { return Promise.resolve(false); } - const entity = this.intersect360ImageIcons(event.offsetX, event.offsetY); - if (entity === undefined) { + const intersection = this.intersect360ImageIcons(event.offsetX, event.offsetY); + if (intersection === undefined) { return Promise.resolve(false); } - return this.enter360ImageInternal(entity); + return this.enter360ImageInternal(intersection.image360); } - public intersect360ImageIcons(offsetX: number, offsetY: number): Image360Entity | undefined { + public intersect360ImageIcons( + offsetX: number, + offsetY: number + ): Image360IconIntersectionData | undefined { const ndcCoordinates = getNormalizedPixelCoordinates(this._domElement, offsetX, offsetY); - const entity = this._image360Facade.intersect( + return this._image360Facade.intersect( new Vector2(ndcCoordinates.x, ndcCoordinates.y), this._activeCameraManager.getCamera() ); - return entity; } public intersect360ImageAnnotations(offsetX: number, offsetY: number): Image360AnnotationIntersection | undefined { @@ -611,11 +614,13 @@ export class Image360ApiHelper { this._interactionState.lastMousePosition = { offsetX, offsetY }; this._image360Facade.allIconsSelected = false; const ndcCoordinates = getNormalizedPixelCoordinates(this._domElement, offsetX, offsetY); - const entity = this._image360Facade.intersect( + const intersection = this._image360Facade.intersect( new Vector2(ndcCoordinates.x, ndcCoordinates.y), this._activeCameraManager.getCamera() ); + const entity = intersection?.image360; + if (entity === this._interactionState.currentImage360Hovered) { entity?.icon.updateHoverSpriteScale(); return; diff --git a/viewer/packages/api/src/public/migration/Cognite3DViewer.ts b/viewer/packages/api/src/public/migration/Cognite3DViewer.ts index f3491ab884d..79728c8a748 100644 --- a/viewer/packages/api/src/public/migration/Cognite3DViewer.ts +++ b/viewer/packages/api/src/public/migration/Cognite3DViewer.ts @@ -48,7 +48,8 @@ import { ResolutionOptions, RenderParameters, AnyIntersection, - AddModelOptions + AddModelOptions, + Image360IconIntersection } from './types'; import { RevealManager } from '../RevealManager'; import { CogniteModel, Image360WithCollection } from '../types'; @@ -1659,7 +1660,7 @@ export class Cognite3DViewer> { - if (this.isIntersecting360Icon(new THREE.Vector2(offsetX, offsetY))) { + if (this.intersect360Icons(new THREE.Vector2(offsetX, offsetY)) !== undefined) { return null; } return this.intersectModels(offsetX, offsetY) as Promise | null>; @@ -1682,8 +1683,9 @@ export class Cognite3DViewer boolean; } ): Promise | undefined> { - if ((options?.stopOnHitting360Icon ?? true) && this.isIntersecting360Icon(pixelCoords)) { - return undefined; + const image360IconIntersection = this.intersect360Icons(pixelCoords); + if (this.intersect360Icons(pixelCoords) !== undefined) { + return image360IconIntersection; } const predicate = options?.predicate; @@ -1740,18 +1742,16 @@ export class Cognite3DViewer | undefined { + const iconIntersection = this._image360ApiHelper?.intersect360ImageIcons(vector.x, vector.y); + if (iconIntersection === undefined) { + return undefined; } - return false; + return { + type: 'image360Icon', + ...iconIntersection + }; } /** diff --git a/viewer/packages/api/src/public/migration/types.ts b/viewer/packages/api/src/public/migration/types.ts index 1b05e759ee2..3293c26b5d9 100644 --- a/viewer/packages/api/src/public/migration/types.ts +++ b/viewer/packages/api/src/public/migration/types.ts @@ -14,7 +14,7 @@ import { EdlOptions } from '@reveal/rendering'; import { Cognite3DViewer } from './Cognite3DViewer'; import { DefaultCameraManager } from '@reveal/camera-manager'; import { CdfModelIdentifier, CommonModelOptions } from '@reveal/data-providers'; -import { Image360AnnotationFilterOptions } from '@reveal/360-images'; +import { Image360, Image360AnnotationFilterOptions, Image360Collection } from '@reveal/360-images'; import type { Vector2, WebGLRenderTarget, WebGLRenderer, Matrix4, Vector3 } from 'three'; import { CustomObjectIntersection } from '@reveal/utilities'; import { ClassicDataSourceType, DataSourceType, DMDataSourceType } from '@reveal/data-providers'; @@ -294,6 +294,34 @@ export type Intersection = | CadIntersection | PointCloudIntersection; +/** + * Represents the result from a 360 intersection test. + * @module @cognite/reveal + * @beta + */ +export type Image360IconIntersection = { + /** + * The intersection type. + */ + type: 'image360Icon'; + /** + * The image360 that was intersected. + */ + image360: Image360; + /** + * The image360 collection that was intersected. + */ + image360Collection: Image360Collection; + /** + * Coordinate of the intersection. + */ + point: Vector3; + /** + * Distance from the camera to the intersection. + */ + distanceToCamera: number; +}; + /** * Represents the result from {@link Cognite3DViewer.getAnyIntersectionFromPixel}. * @module @cognite/reveal @@ -302,6 +330,7 @@ export type Intersection = export type AnyIntersection = | CadIntersection | PointCloudIntersection + | Image360IconIntersection | CustomObjectIntersection; /** diff --git a/viewer/reveal.api.md b/viewer/reveal.api.md index 171b38e667c..b3c2b235b51 100644 --- a/viewer/reveal.api.md +++ b/viewer/reveal.api.md @@ -58,7 +58,7 @@ export class AnnotationIdPointCloudObjectCollection extends PointCloudAnnotation } // @beta -export type AnyIntersection = CadIntersection | PointCloudIntersection | CustomObjectIntersection; +export type AnyIntersection = CadIntersection | PointCloudIntersection | Image360IconIntersection | CustomObjectIntersection; // @public export interface AreaCollection { @@ -1307,6 +1307,15 @@ export type Image360EnteredDelegate = (image360: Image360, revision: Image360Rev // @public export type Image360ExitedDelegate = () => void; +// @beta +export type Image360IconIntersection = { + type: 'image360Icon'; + image360: Image360; + image360Collection: Image360Collection; + point: Vector3; + distanceToCamera: number; +}; + // @public export type Image360IconStyle = { color?: Color;