diff --git a/react-components/package.json b/react-components/package.json index 603b26fe161..bc01cf3a582 100644 --- a/react-components/package.json +++ b/react-components/package.json @@ -30,7 +30,7 @@ }, "peerDependencies": { "@cognite/cogs.js": ">=9.84.3", - "@cognite/reveal": "4.15.0", + "@cognite/reveal": "4.15.1", "react": ">=18", "react-dom": ">=18", "styled-components": ">=5" @@ -44,7 +44,7 @@ "@cognite/cdf-i18n-utils": "^0.7.5", "@cognite/cdf-utilities": "^3.6.0", "@cognite/cogs.js": "^9.84.3", - "@cognite/reveal": "^4.15.0", + "@cognite/reveal": "^4.15.1", "@cognite/sdk": "^9.13.0", "@playwright/test": "^1.43.1", "@storybook/addon-essentials": "^8.0.9", diff --git a/react-components/src/architecture/base/commands/BaseTool.ts b/react-components/src/architecture/base/commands/BaseTool.ts index ef887ee11d6..38828620367 100644 --- a/react-components/src/architecture/base/commands/BaseTool.ts +++ b/react-components/src/architecture/base/commands/BaseTool.ts @@ -138,8 +138,7 @@ export abstract class BaseTool extends RenderTargetCommand { event: PointerEvent | WheelEvent, domainObjectPredicate?: (domainObject: DomainObject) => boolean ): Promise { - const { renderTarget } = this; - const { viewer } = renderTarget; + const viewer = this.renderTarget.viewer; const point = viewer.getPixelCoordinatesFromEvent(event); @@ -155,7 +154,7 @@ export abstract class BaseTool extends RenderTargetCommand { if (!(customObject instanceof ThreeView)) { return false; } - return domainObjectPredicate(customObject.domainObject); + return domainObjectPredicate(customObject.domainObject as DomainObject); }; return await viewer.getAnyIntersectionFromPixel(point, { predicate }); } diff --git a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts index eafa130b3f6..55a1e3a9561 100644 --- a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts +++ b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts @@ -30,6 +30,8 @@ import { CommandsUpdater } from '../reactUpdaters/CommandsUpdater'; import { Range3 } from '../utilities/geometry/Range3'; import { getBoundingBoxFromPlanes } from '../utilities/geometry/getBoundingBoxFromPlanes'; import { Changes } from '../domainObjectsHelpers/Changes'; +import { type CogniteClient } from '@cognite/sdk/dist/src'; +import { FdmSDK } from '../../../utilities/FdmSDK'; const DIRECTIONAL_LIGHT_NAME = 'DirectionalLight'; @@ -39,6 +41,8 @@ export class RevealRenderTarget { // ================================================== private readonly _viewer: Cognite3DViewer; + private readonly _sdk: CogniteClient; + private readonly _fdmSdk: FdmSDK; private readonly _commandsController: CommandsController; private readonly _rootDomainObject: RootDomainObject; private _ambientLight: AmbientLight | undefined; @@ -52,8 +56,10 @@ export class RevealRenderTarget { // CONSTRUCTOR // ================================================== - constructor(viewer: Cognite3DViewer) { + constructor(viewer: Cognite3DViewer, sdk: CogniteClient) { this._viewer = viewer; + this._sdk = sdk; + this._fdmSdk = new FdmSDK(sdk); const cameraManager = this.cameraManager; if (!isFlexibleCameraManager(cameraManager)) { @@ -77,6 +83,14 @@ export class RevealRenderTarget { return this._viewer; } + public get sdk(): CogniteClient { + return this._sdk; + } + + public get fdmSdk(): FdmSDK { + return this._fdmSdk; + } + public get config(): BaseRevealConfig | undefined { return this._config; } diff --git a/react-components/src/architecture/base/views/BaseView.ts b/react-components/src/architecture/base/views/BaseView.ts index dfe435a3489..8361d6b0037 100644 --- a/react-components/src/architecture/base/views/BaseView.ts +++ b/react-components/src/architecture/base/views/BaseView.ts @@ -9,12 +9,12 @@ import { type DomainObjectChange } from '../domainObjectsHelpers/DomainObjectCha * Represents the observer in the Observer pattern * It provides common functionality for all types of views. */ -export abstract class BaseView { +export abstract class BaseView { // ================================================== // INSTANCE FIELDS // ================================================== - private _domainObject: DomainObject | undefined = undefined; + private _domainObject: DomainObjectType | undefined = undefined; // ================================================== // INSTANCE PROPERTIES @@ -24,7 +24,7 @@ export abstract class BaseView { return this._domainObject !== undefined; } - public get domainObject(): DomainObject { + public get domainObject(): DomainObjectType { if (this._domainObject === undefined) { throw Error('The DomainObject is missing in the view'); } @@ -92,7 +92,7 @@ export abstract class BaseView { // INSTANCE METHODS // ================================================== - public setDomainObject(domainObject: DomainObject): void { + public setDomainObject(domainObject: DomainObjectType): void { this._domainObject = domainObject; } } diff --git a/react-components/src/architecture/base/views/GroupThreeView.ts b/react-components/src/architecture/base/views/GroupThreeView.ts index afcde5e5f04..3913af54377 100644 --- a/react-components/src/architecture/base/views/GroupThreeView.ts +++ b/react-components/src/architecture/base/views/GroupThreeView.ts @@ -13,6 +13,7 @@ import { } from '@cognite/reveal'; import { type DomainObjectIntersection } from '../domainObjectsHelpers/DomainObjectIntersection'; import { VisualDomainObject } from '../domainObjects/VisualDomainObject'; +import { type DomainObject } from '../domainObjects/DomainObject'; /** * Represents an abstract class for a Three.js view that renders an Object3D. @@ -24,7 +25,10 @@ import { VisualDomainObject } from '../domainObjects/VisualDomainObject'; * - calculateBoundingBox() to calculate the bounding box if you don not relay on three.js. */ -export abstract class GroupThreeView extends ThreeView implements ICustomObject { +export abstract class GroupThreeView + extends ThreeView + implements ICustomObject +{ // ================================================== // INSTANCE FIELDS // ================================================== diff --git a/react-components/src/architecture/base/views/ThreeView.ts b/react-components/src/architecture/base/views/ThreeView.ts index 2cb0d6d26fe..dfee635291c 100644 --- a/react-components/src/architecture/base/views/ThreeView.ts +++ b/react-components/src/architecture/base/views/ThreeView.ts @@ -17,7 +17,9 @@ import { type PerspectiveCamera, type Box3 } from 'three'; * It can for instance be a view that changes something on another view, dor instance texture on a surface or whatever. * I just wanted to make it ready for some corner cases I have seen during a long time as 3D developer. */ -export abstract class ThreeView extends BaseView { +export abstract class ThreeView< + DomainObjectType extends DomainObject = DomainObject +> extends BaseView { // ================================================== // INSTANCE FIELDS // ================================================== @@ -112,7 +114,7 @@ export abstract class ThreeView extends BaseView { this._boundingBox = undefined; } - public attach(domainObject: DomainObject, renderTarget: RevealRenderTarget): void { + public attach(domainObject: DomainObjectType, renderTarget: RevealRenderTarget): void { super.setDomainObject(domainObject); this._renderTarget = renderTarget; } diff --git a/react-components/src/architecture/concrete/config/StoryBookConfig.ts b/react-components/src/architecture/concrete/config/StoryBookConfig.ts index ae62b078558..1cfc959da63 100644 --- a/react-components/src/architecture/concrete/config/StoryBookConfig.ts +++ b/react-components/src/architecture/concrete/config/StoryBookConfig.ts @@ -20,6 +20,7 @@ import { ToggleMetricUnitsCommand } from '../../base/concreteCommands/ToggleMetr import { MeasurementTool } from '../measurements/MeasurementTool'; import { ClipTool } from '../clipping/ClipTool'; import { KeyboardSpeedCommand } from '../../base/concreteCommands/KeyboardSpeedCommand'; +import { ObservationsTool } from '../observationsDomainObject/ObservationsTool'; export class StoryBookConfig extends BaseRevealConfig { // ================================================== @@ -43,6 +44,7 @@ export class StoryBookConfig extends BaseRevealConfig { new ExampleTool(), new MeasurementTool(), new ClipTool(), + new ObservationsTool(), undefined, new SetTerrainVisibleCommand(), new UpdateTerrainCommand() diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts index 8df8fd2e8f1..87c2a5cc9b8 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts @@ -16,15 +16,11 @@ import { import { type DomainObjectIntersection } from '../../base/domainObjectsHelpers/DomainObjectIntersection'; import { WHITE_COLOR } from '../../base/utilities/colors/colorExtensions'; -export class ExampleView extends GroupThreeView { +export class ExampleView extends GroupThreeView { // ================================================== // INSTANCE PROPERTIES // ================================================== - public override get domainObject(): ExampleDomainObject { - return super.domainObject as ExampleDomainObject; - } - protected override get style(): ExampleRenderStyle { return super.style as ExampleRenderStyle; } diff --git a/react-components/src/architecture/concrete/observationsDomainObject/ObservationsDomainObject.ts b/react-components/src/architecture/concrete/observationsDomainObject/ObservationsDomainObject.ts new file mode 100644 index 00000000000..540fa4ff125 --- /dev/null +++ b/react-components/src/architecture/concrete/observationsDomainObject/ObservationsDomainObject.ts @@ -0,0 +1,79 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { CDF_TO_VIEWER_TRANSFORMATION, type Overlay3D, Overlay3DCollection } from '@cognite/reveal'; +import { OBSERVATION_SOURCE, type ObservationProperties, type Observation } from './models'; +import { VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; +import { type ThreeView } from '../../base/views/ThreeView'; +import { ObservationsView } from './ObservationsView'; +import { type TranslateKey } from '../../base/utilities/TranslateKey'; +import { type FdmSDK, type InstanceFilter } from '../../../utilities/FdmSDK'; +import { Vector3 } from 'three'; +import { DEFAULT_OVERLAY_COLOR } from './constants'; +import { Changes } from '../../base/domainObjectsHelpers/Changes'; + +export class ObservationsDomainObject extends VisualDomainObject { + private _selectedOverlay: Overlay3D | undefined; + + private readonly _collection = new Overlay3DCollection([], { + defaultOverlayColor: DEFAULT_OVERLAY_COLOR + }); + + constructor(fdmSdk: FdmSDK) { + super(); + void fetchObservations(fdmSdk).then((observations) => { + this.initializeCollection(observations); + this.notify(Changes.geometry); + }); + } + + public override get typeName(): TranslateKey { + return { fallback: ObservationsDomainObject.name }; + } + + protected override createThreeView(): ThreeView | undefined { + return new ObservationsView(); + } + + private initializeCollection(observations: Observation[]): void { + const observationOverlays = observations.map((observation) => { + const position = new Vector3( + observation.properties.positionX, + observation.properties.positionY, + observation.properties.positionZ + ).applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION); + + return { + position, + content: observation + }; + }); + + this._collection.addOverlays(observationOverlays); + } + + public get overlayCollection(): Overlay3DCollection { + return this._collection; + } + + public getSelectedOverlay(): Overlay3D | undefined { + return this._selectedOverlay; + } + + public setSelectedOverlay(overlay: Overlay3D | undefined): void { + this._selectedOverlay = overlay; + this.notify(Changes.selected); + } +} + +async function fetchObservations(fdmSdk: FdmSDK): Promise { + const observationsFilter: InstanceFilter = {}; + + const observationResult = await fdmSdk.filterAllInstances( + observationsFilter, + 'node', + OBSERVATION_SOURCE + ); + + return observationResult.instances; +} diff --git a/react-components/src/architecture/concrete/observationsDomainObject/ObservationsTool.ts b/react-components/src/architecture/concrete/observationsDomainObject/ObservationsTool.ts new file mode 100644 index 00000000000..da9a7765cd2 --- /dev/null +++ b/react-components/src/architecture/concrete/observationsDomainObject/ObservationsTool.ts @@ -0,0 +1,58 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type IconType } from '@cognite/cogs.js'; +import { type TranslateKey } from '../../base/utilities/TranslateKey'; +import { ObservationsDomainObject } from './ObservationsDomainObject'; +import { BaseEditTool } from '../../base/commands/BaseEditTool'; +import { type VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; + +export class ObservationsTool extends BaseEditTool { + public override get icon(): IconType { + return 'Location'; + } + + public override get tooltip(): TranslateKey { + return { fallback: 'Show observations' }; + } + + public override onActivate(): void { + let domainObject = this.getObservationsDomainObject(); + if (domainObject === undefined) { + domainObject = new ObservationsDomainObject(this.renderTarget.fdmSdk); + this.renderTarget.rootDomainObject.addChild(domainObject); + } + domainObject.setVisibleInteractive(true, this.renderTarget); + } + + public override onDeactivate(): void { + this.getObservationsDomainObject()?.setVisibleInteractive(false, this.renderTarget); + } + + public override async onClick(event: PointerEvent): Promise { + const intersection = await this.getIntersection(event); + + const domainObject = this.getIntersectedSelectableDomainObject(intersection); + if (!(domainObject instanceof ObservationsDomainObject)) { + await super.onClick(event); + return; + } + + const normalizedCoords = this.getNormalizedPixelCoordinates(event); + + const intersectedOverlay = domainObject.overlayCollection.intersectOverlays( + normalizedCoords, + this.renderTarget.camera + ); + + domainObject.setSelectedOverlay(intersectedOverlay); + } + + protected override canBeSelected(domainObject: VisualDomainObject): boolean { + return domainObject instanceof ObservationsDomainObject; + } + + public getObservationsDomainObject(): ObservationsDomainObject | undefined { + return this.rootDomainObject.getDescendantByType(ObservationsDomainObject); + } +} diff --git a/react-components/src/architecture/concrete/observationsDomainObject/ObservationsView.ts b/react-components/src/architecture/concrete/observationsDomainObject/ObservationsView.ts new file mode 100644 index 00000000000..441d03a4f57 --- /dev/null +++ b/react-components/src/architecture/concrete/observationsDomainObject/ObservationsView.ts @@ -0,0 +1,77 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { Box3 } from 'three'; +import { type ObservationsDomainObject } from './ObservationsDomainObject'; +import { GroupThreeView } from '../../base/views/GroupThreeView'; +import { type CustomObjectIntersectInput, type CustomObjectIntersection } from '@cognite/reveal'; +import { type DomainObjectIntersection } from '../../base/domainObjectsHelpers/DomainObjectIntersection'; +import { Changes } from '../../base/domainObjectsHelpers/Changes'; +import { DEFAULT_OVERLAY_COLOR, SELECTED_OVERLAY_COLOR } from './constants'; +import { type DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; + +export class ObservationsView extends GroupThreeView { + protected override calculateBoundingBox(): Box3 { + return this.domainObject.overlayCollection + .getOverlays() + .reduce((box, overlay) => box.expandByPoint(overlay.getPosition()), new Box3()); + } + + protected override addChildren(): void { + this.addChild(this.domainObject.overlayCollection); + } + + public override update(change: DomainObjectChange): void { + super.update(change); + if (change.isChanged(Changes.selected)) { + this.domainObject.overlayCollection.getOverlays().forEach((overlay) => { + overlay.setColor(DEFAULT_OVERLAY_COLOR); + }); + + const overlay = this.domainObject.getSelectedOverlay(); + overlay?.setColor(SELECTED_OVERLAY_COLOR); + + this.renderTarget.invalidate(); + } + } + + public override intersectIfCloser( + intersectInput: CustomObjectIntersectInput, + closestDistance: number | undefined + ): undefined | CustomObjectIntersection { + const { domainObject } = this; + + const intersection = this.domainObject.overlayCollection.intersectOverlays( + intersectInput.normalizedCoords, + intersectInput.camera + ); + + if (intersection === undefined) { + return undefined; + } + + const point = intersection.getPosition(); + + const distanceToCamera = point.distanceTo(intersectInput.raycaster.ray.origin); + if (closestDistance !== undefined && closestDistance < distanceToCamera) { + return undefined; + } + + if (domainObject.useClippingInIntersection && !intersectInput.isVisible(point)) { + return undefined; + } + + const customObjectIntersection: DomainObjectIntersection = { + type: 'customObject', + point, + distanceToCamera, + customObject: this, + domainObject + }; + + if (this.shouldPickBoundingBox) { + customObjectIntersection.boundingBox = this.boundingBox; + } + return customObjectIntersection; + } +} diff --git a/react-components/src/architecture/concrete/observationsDomainObject/constants.ts b/react-components/src/architecture/concrete/observationsDomainObject/constants.ts new file mode 100644 index 00000000000..bab3e2c3073 --- /dev/null +++ b/react-components/src/architecture/concrete/observationsDomainObject/constants.ts @@ -0,0 +1,7 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { Color } from 'three'; + +export const DEFAULT_OVERLAY_COLOR = new Color('lightblue'); +export const SELECTED_OVERLAY_COLOR = new Color('red'); diff --git a/react-components/src/architecture/concrete/observationsDomainObject/models.ts b/react-components/src/architecture/concrete/observationsDomainObject/models.ts new file mode 100644 index 00000000000..f402f148667 --- /dev/null +++ b/react-components/src/architecture/concrete/observationsDomainObject/models.ts @@ -0,0 +1,57 @@ +/*! + * Copyright 2024 Cognite AS + */ +import { type DmsUniqueIdentifier, type FdmNode, type Source } from '../../../utilities/FdmSDK'; + +export type ObservationProperties = { + // "ID as the node appears in the Source system" + sourceId: string; + // "Name of the source system node comes from" + source: string; + // "Title or name of the node" + title: string; + // "Long description of the node" + description: string; + // "Text based labels for generic use" + labels: string[]; + // "Visibility of node (PUBLIC, PRIVATE, PROTECTED)" + visibility: string; + // "Who created this node?" + createdBy: string; + // "Who was the last person to update this node?" + updatedBy: string; + // "Is this item archived, and therefore hidden from most UIs?" + isArchived: boolean; + // "The status of the observation (draft, completed, sent)" + status: string; + // "External ID of the associated CDF Asset" + asset: DmsUniqueIdentifier; + // "List of associated files" + files: DmsUniqueIdentifier[]; + // "description of how the observation was troubleshooted" + troubleshooting: string; + // "Priority of the observation (Urgent, High ...)" + priority: string; + // "The observation type (Malfunction report, Maintenance request, etc.)" + type: string; + // "3D position" + positionX: number; + positionY: number; + positionZ: number; + // "Comments" + comments: [CommentProperties]; +}; + +export type CommentProperties = { + createdBy: string; + text: string; +}; + +export type Observation = FdmNode; + +export const OBSERVATION_SOURCE: Source = { + type: 'view', + version: '3c207ca2355dbb', + externalId: 'Observation', + space: 'observations' +}; diff --git a/react-components/src/architecture/concrete/primitives/box/BoxView.ts b/react-components/src/architecture/concrete/primitives/box/BoxView.ts index ca4dc586a8d..7d452133a97 100644 --- a/react-components/src/architecture/concrete/primitives/box/BoxView.ts +++ b/react-components/src/architecture/concrete/primitives/box/BoxView.ts @@ -55,7 +55,7 @@ const TOP_FACE = new BoxFace(2); const CIRCULAR_SEGMENTS = 32; const RENDER_ORDER = 100; -export class BoxView extends GroupThreeView { +export class BoxView extends GroupThreeView { // ================================================== // INSTANCE FIELDS // ================================================== @@ -67,10 +67,6 @@ export class BoxView extends GroupThreeView { // INSTANCE PROPERTIES // ================================================== - public override get domainObject(): BoxDomainObject { - return super.domainObject as BoxDomainObject; - } - protected override get style(): BoxRenderStyle { return super.style as BoxRenderStyle; } diff --git a/react-components/src/architecture/concrete/primitives/line/LineView.ts b/react-components/src/architecture/concrete/primitives/line/LineView.ts index 6b198f5b571..4ba5677ab6e 100644 --- a/react-components/src/architecture/concrete/primitives/line/LineView.ts +++ b/react-components/src/architecture/concrete/primitives/line/LineView.ts @@ -41,15 +41,11 @@ import { BoxView } from '../box/BoxView'; const CYLINDER_DEFAULT_AXIS = new Vector3(0, 1, 0); const RENDER_ORDER = 100; -export class LineView extends GroupThreeView { +export class LineView extends GroupThreeView { // ================================================== // INSTANCE PROPERTIES // ================================================== - public override get domainObject(): LineDomainObject { - return super.domainObject as LineDomainObject; - } - protected override get style(): LineRenderStyle { return super.style as LineRenderStyle; } diff --git a/react-components/src/architecture/concrete/primitives/plane/PlaneView.ts b/react-components/src/architecture/concrete/primitives/plane/PlaneView.ts index 020a95ab9c6..6dd7906b863 100644 --- a/react-components/src/architecture/concrete/primitives/plane/PlaneView.ts +++ b/react-components/src/architecture/concrete/primitives/plane/PlaneView.ts @@ -32,7 +32,7 @@ import { FocusType } from '../../../base/domainObjectsHelpers/FocusType'; import { type DomainObjectIntersection } from '../../../base/domainObjectsHelpers/DomainObjectIntersection'; import { PrimitiveType } from '../PrimitiveType'; -export class PlaneView extends GroupThreeView { +export class PlaneView extends GroupThreeView { // ================================================== // INSTANCE FIELDS // ================================================== @@ -44,10 +44,6 @@ export class PlaneView extends GroupThreeView { // INSTANCE PROPERTIES // ================================================== - public override get domainObject(): PlaneDomainObject { - return super.domainObject as PlaneDomainObject; - } - protected override get style(): PlaneRenderStyle { return super.style as PlaneRenderStyle; } diff --git a/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts b/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts index 04d0cf777c8..012855c696f 100644 --- a/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts +++ b/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts @@ -35,15 +35,11 @@ import { type RegularGrid2 } from './geometry/RegularGrid2'; const SOLID_NAME = 'Solid'; const CONTOURS_NAME = 'Contour'; -export class TerrainThreeView extends GroupThreeView { +export class TerrainThreeView extends GroupThreeView { // ================================================== // INSTANCE PROPERTIES // ================================================== - public override get domainObject(): TerrainDomainObject { - return super.domainObject as TerrainDomainObject; - } - protected override get style(): TerrainRenderStyle { return super.style as TerrainRenderStyle; } diff --git a/react-components/src/components/Architecture/CommandButton.tsx b/react-components/src/components/Architecture/CommandButton.tsx index 9130425fc7e..f006bc5b910 100644 --- a/react-components/src/components/Architecture/CommandButton.tsx +++ b/react-components/src/components/Architecture/CommandButton.tsx @@ -10,6 +10,14 @@ import { type BaseCommand } from '../../architecture/base/commands/BaseCommand'; import { getButtonType, getDefaultCommand, getIcon, getTooltipPlacement } from './utilities'; import { LabelWithShortcut } from './LabelWithShortcut'; +export const createCommandButton = ( + commandConstructor: () => BaseCommand, + isHorizontal = false +): ReactElement => { + const command = useMemo(commandConstructor, []); + return ; +}; + export const CommandButton = ({ inputCommand, isHorizontal = false diff --git a/react-components/src/components/Architecture/RevealButtons.tsx b/react-components/src/components/Architecture/RevealButtons.tsx index 290b4561333..5566e8f8f5f 100644 --- a/react-components/src/components/Architecture/RevealButtons.tsx +++ b/react-components/src/components/Architecture/RevealButtons.tsx @@ -10,20 +10,31 @@ import { SetFlexibleControlsTypeCommand } from '../../architecture/base/concrete import { SetAxisVisibleCommand } from '../../architecture/concrete/axis/SetAxisVisibleCommand'; import { ClipTool } from '../../architecture/concrete/clipping/ClipTool'; import { MeasurementTool } from '../../architecture/concrete/measurements/MeasurementTool'; +import { ObservationsTool } from '../../architecture/concrete/observationsDomainObject/ObservationsTool'; import { KeyboardSpeedCommand } from '../../architecture/base/concreteCommands/KeyboardSpeedCommand'; -import { CreateButton } from './CommandButtons'; +import { createCommandButton } from './CommandButton'; export class RevealButtons { - static FitView = (): ReactElement => CreateButton(new FitViewCommand()); - static NavigationTool = (): ReactElement => CreateButton(new NavigationTool()); - static SetAxisVisible = (): ReactElement => CreateButton(new SetAxisVisibleCommand()); - static Measurement = (): ReactElement => CreateButton(new MeasurementTool()); - static Clip = (): ReactElement => CreateButton(new ClipTool()); - static KeyboardSpeed = (): ReactElement => CreateButton(new KeyboardSpeedCommand()); + static FitView = (): ReactElement => createCommandButton(() => new FitViewCommand()); + + static NavigationTool = (): ReactElement => createCommandButton(() => new NavigationTool()); + + static SetAxisVisible = (): ReactElement => + createCommandButton(() => new SetAxisVisibleCommand()); + + static Measurement = (): ReactElement => createCommandButton(() => new MeasurementTool()); + + static Clip = (): ReactElement => createCommandButton(() => new ClipTool()); static SetFlexibleControlsTypeOrbit = (): ReactElement => - CreateButton(new SetFlexibleControlsTypeCommand(FlexibleControlsType.Orbit)); + createCommandButton(() => new SetFlexibleControlsTypeCommand(FlexibleControlsType.Orbit)); static SetFlexibleControlsTypeFirstPerson = (): ReactElement => - CreateButton(new SetFlexibleControlsTypeCommand(FlexibleControlsType.FirstPerson)); + createCommandButton(() => new SetFlexibleControlsTypeCommand(FlexibleControlsType.FirstPerson)); + + static Observations = (): ReactElement => { + return createCommandButton(() => new ObservationsTool()); + }; + + static KeyboardSpeed = (): ReactElement => createCommandButton(() => new KeyboardSpeedCommand()); } diff --git a/react-components/src/components/RevealContext/RevealContext.tsx b/react-components/src/components/RevealContext/RevealContext.tsx index c76929d5bb5..d36be8d6b99 100644 --- a/react-components/src/components/RevealContext/RevealContext.tsx +++ b/react-components/src/components/RevealContext/RevealContext.tsx @@ -106,7 +106,7 @@ const useRevealFromKeepAlive = ({ useFlexibleCameraManager: true, hasEventListeners: false }); - renderTarget = new RevealRenderTarget(viewer); + renderTarget = new RevealRenderTarget(viewer, sdk); if (revealKeepAliveData !== undefined) { revealKeepAliveData.renderTargetRef.current = renderTarget; } diff --git a/react-components/src/components/RuleBasedOutputs/types.ts b/react-components/src/components/RuleBasedOutputs/types.ts index a3fb33c0314..a4fa20cbedc 100644 --- a/react-components/src/components/RuleBasedOutputs/types.ts +++ b/react-components/src/components/RuleBasedOutputs/types.ts @@ -3,9 +3,9 @@ */ import { type TreeIndexNodeCollection, type NumericRange } from '@cognite/reveal'; -import { type FdmNode, type EdgeItem } from '../../utilities/FdmSDK'; +import { type FdmNode, type EdgeItem, type DmsUniqueIdentifier } from '../../utilities/FdmSDK'; import { type AssetStylingGroup, type FdmPropertyType } from '../Reveal3DResources/types'; -import { type Datapoints, type Asset, type Timeseries, type ExternalId } from '@cognite/sdk'; +import { type Datapoints, type Asset, type Timeseries } from '@cognite/sdk'; // =========== RULE BASED OUTPUT DATA MODEL @@ -220,11 +220,6 @@ export type SimpleSource = { version: string; } & DmsUniqueIdentifier; -export type DmsUniqueIdentifier = { - space: Space; - externalId: ExternalId; -}; - export type ViewQueryFilter = { view: Source; }; diff --git a/react-components/src/utilities/FdmSDK.ts b/react-components/src/utilities/FdmSDK.ts index 25ac69aa581..afaaeade356 100644 --- a/react-components/src/utilities/FdmSDK.ts +++ b/react-components/src/utilities/FdmSDK.ts @@ -283,12 +283,6 @@ export class FdmSDK { filter?: InstanceFilter, properties?: string[] ): Promise<{ instances: Array | NodeItem> }> { - function makeSureNonEmptyFilterForRequest( - filter: InstanceFilter | undefined - ): InstanceFilter | undefined { - return filter !== undefined && Object.keys(filter).length === 0 ? undefined : filter; - } - filter = makeSureNonEmptyFilterForRequest(filter); const data: any = { @@ -315,7 +309,7 @@ export class FdmSDK { // eslint-disable-next-line no-dupe-class-members public async filterInstances>( - filter: InstanceFilter, + filter: InstanceFilter | undefined, instanceType: InstanceType, source?: Source, cursor?: string @@ -326,7 +320,7 @@ export class FdmSDK { // eslint-disable-next-line no-dupe-class-members public async filterInstances>( - filter: InstanceFilter, + filter: InstanceFilter | undefined, instanceType: 'node', source?: Source, cursor?: string @@ -334,7 +328,7 @@ export class FdmSDK { // eslint-disable-next-line no-dupe-class-members public async filterInstances>( - filter: InstanceFilter, + filter: InstanceFilter | undefined, instanceType: 'edge', source?: Source, cursor?: string @@ -342,7 +336,7 @@ export class FdmSDK { // eslint-disable-next-line no-dupe-class-members public async filterInstances>( - filter: InstanceFilter, + filter: InstanceFilter | undefined, instanceType: InstanceType, source: Source, cursor?: string @@ -399,10 +393,12 @@ export class FdmSDK { // eslint-disable-next-line no-dupe-class-members public async filterAllInstances>( - filter: InstanceFilter, + filter: InstanceFilter | undefined, instanceType: InstanceType, source?: Source ): Promise<{ instances: Array | FdmNode> }> { + filter = makeSureNonEmptyFilterForRequest(filter); + let mappings = await this.filterInstances(filter, instanceType, source); while (mappings.nextCursor !== undefined) { @@ -558,3 +554,9 @@ function hoistInstanceProperties( } }); } + +function makeSureNonEmptyFilterForRequest( + filter: InstanceFilter | undefined +): InstanceFilter | undefined { + return filter !== undefined && Object.keys(filter).length === 0 ? undefined : filter; +} diff --git a/react-components/stories/utilities/RevealStoryContainer.tsx b/react-components/stories/utilities/RevealStoryContainer.tsx index d232733b117..b773914b767 100644 --- a/react-components/stories/utilities/RevealStoryContainer.tsx +++ b/react-components/stories/utilities/RevealStoryContainer.tsx @@ -40,21 +40,20 @@ export const RevealStoryContext = ({ const isLocal = sdkInstance.project === ''; const renderTarget = useMemo(() => { - if (viewer !== undefined) { - return new RevealRenderTarget(viewer); - } else if (isLocal) { - const newViewer = new Cognite3DViewer({ + if (viewer === undefined) { + viewer = new Cognite3DViewer({ ...rest.viewerOptions, sdk: sdkInstance, // @ts-expect-error use local models - _localModels: true, + _localModels: isLocal, hasEventListeners: false, useFlexibleCameraManager: true }); - const renderTarget = new RevealRenderTarget(newViewer); - renderTarget.setConfig(new StoryBookConfig()); - return renderTarget; } + + const renderTarget = new RevealRenderTarget(viewer, sdkInstance); + renderTarget.setConfig(new StoryBookConfig()); + return renderTarget; }, [viewer]); const renderTargetRef = useRef(renderTarget); diff --git a/react-components/yarn.lock b/react-components/yarn.lock index 1331bd07c71..962047edda1 100644 --- a/react-components/yarn.lock +++ b/react-components/yarn.lock @@ -1747,7 +1747,7 @@ __metadata: "@cognite/cdf-i18n-utils": "npm:^0.7.5" "@cognite/cdf-utilities": "npm:^3.6.0" "@cognite/cogs.js": "npm:^9.84.3" - "@cognite/reveal": "npm:^4.15.0" + "@cognite/reveal": "npm:^4.15.1" "@cognite/sdk": "npm:^9.13.0" "@playwright/test": "npm:^1.43.1" "@storybook/addon-essentials": "npm:^8.0.9" @@ -1802,16 +1802,16 @@ __metadata: vitest: "npm:^1.5.3" peerDependencies: "@cognite/cogs.js": ">=9.84.3" - "@cognite/reveal": 4.15.0 + "@cognite/reveal": 4.15.1 react: ">=18" react-dom: ">=18" styled-components: ">=5" languageName: unknown linkType: soft -"@cognite/reveal@npm:^4.15.0": - version: 4.15.0 - resolution: "@cognite/reveal@npm:4.15.0" +"@cognite/reveal@npm:^4.15.1": + version: 4.15.1 + resolution: "@cognite/reveal@npm:4.15.1" dependencies: "@rajesh896/broprint.js": "npm:^2.1.1" "@tweenjs/tween.js": "npm:^23.1.1" @@ -1831,7 +1831,7 @@ __metadata: peerDependencies: "@cognite/sdk": ^7.16.0 || ^8.0.0 three: 0.165.0 - checksum: 10/5d8111cf3cc5a2424a643fd0d743764a90b56ffb7602fccc3dc07aace1f9857eb514abe765a6fc2cd6ba8fdbe069dea22a1ba46a00526b6800bf8f947f64f3b0 + checksum: 10/0dc5ee79866cfc25043d04c687ade643674dd42f4587a2950e6cea080a2331fbe8d878673c74ffbf910018959a25f32c6a2f65518ebf25457982fadc1c89921e languageName: node linkType: hard