From 2b5ebb77969f02c1b32f3bffa5a4f991615a863b Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Wed, 5 Jun 2024 13:32:57 +0200 Subject: [PATCH 1/6] Update example code --- .../exampleDomainObject/ExampleRenderStyle.ts | 1 + .../exampleDomainObject/ExampleTool.ts | 6 ++- .../exampleDomainObject/ExampleView.ts | 7 ++- .../commands/ShowExamplesOnTopCommand.ts | 54 +++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleRenderStyle.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleRenderStyle.ts index 6e6581944b7..652055e7b27 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleRenderStyle.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleRenderStyle.ts @@ -12,6 +12,7 @@ export class ExampleRenderStyle extends RenderStyle { public radius = 1; public opacity = 0.75; + public depthTest = true; // ================================================== // OVERRIDES of BaseStyle diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts index b50190a282e..c3a284e697d 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts @@ -14,6 +14,8 @@ import { clamp } from 'lodash'; import { type DomainObject } from '../../base/domainObjects/DomainObject'; import { type HSL } from 'three'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; +import { ShowExamplesOnTopCommand } from './commands/ShowExamplesOnTopCommand'; +import { ToogleMetricUnitsCommand } from '../../base/concreteCommands/ToogleMetricUnitsCommand'; export class ExampleTool extends BaseEditTool { // ================================================== @@ -113,7 +115,9 @@ export class ExampleTool extends BaseEditTool { return [ new ResetAllExamplesCommand(), new ShowAllExamplesCommand(), - new DeleteAllExamplesCommand() + new DeleteAllExamplesCommand(), + new ShowExamplesOnTopCommand(), + new ToogleMetricUnitsCommand() ]; } diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts index 21af869ae04..4e1cfd8a3aa 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts @@ -46,6 +46,10 @@ export class ExampleView extends GroupThreeView { // OVERRIDES of GroupThreeView // ================================================== + public override get useDepthTest(): boolean { + return this.style.depthTest; + } + protected override addChildren(): void { const { domainObject, style } = this; @@ -57,7 +61,8 @@ export class ExampleView extends GroupThreeView { emissiveIntensity: domainObject.isSelected ? 0.4 : 0.0, shininess: 5, opacity: style.opacity, - transparent: true + transparent: true, + depthTest: style.depthTest }); const sphere = new Mesh(geometry, material); const center = domainObject.center.clone(); diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts new file mode 100644 index 00000000000..ba888566b30 --- /dev/null +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts @@ -0,0 +1,54 @@ +/*! + * Copyright 2024 Cognite AS + * BaseTool: Base class for the tool are used to interact with the render target. + */ + +import { RenderTargetCommand } from '../../../base/commands/RenderTargetCommand'; +import { Changes } from '../../../base/domainObjectsHelpers/Changes'; +import { type TranslateKey } from '../../../base/utilities/TranslateKey'; +import { ExampleDomainObject } from '../ExampleDomainObject'; + +export class ShowExamplesOnTopCommand extends RenderTargetCommand { + // ================================================== + // OVERRIDES + // ================================================== + + public override get tooltip(): TranslateKey { + return { key: 'EXAMPLES_SHOW_ON_TOP', fallback: 'Show all examples on top' }; + } + + public override get icon(): string { + return 'EyeShow'; + } + + public override get isEnabled(): boolean { + const domainObject = this.rootDomainObject.getDescendantByType(ExampleDomainObject); + return domainObject !== undefined; + } + + public override get isChecked(): boolean { + return !this.getDepthTest(); + } + + protected override invokeCore(): boolean { + const depthTest = this.getDepthTest(); + for (const domainObject of this.rootDomainObject.getDescendantsByType(ExampleDomainObject)) { + const style = domainObject.renderStyle; + style.depthTest = !depthTest; + domainObject.notify(Changes.renderStyle); + } + return true; + } + + // ================================================== + // INSTANCE METHODS + // ================================================== + + public getDepthTest(): boolean { + const domainObject = this.rootDomainObject.getDescendantByType(ExampleDomainObject); + if (domainObject === undefined) { + return false; + } + return domainObject.renderStyle.depthTest; + } +} From 6c65c63a527f88221c425f7acf8c2a7ea6ad7709 Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Wed, 5 Jun 2024 13:40:40 +0200 Subject: [PATCH 2/6] Add comments and reuse --- .../boxDomainObject/ShowMeasurmentsOnTopCommand.ts | 11 +++++++---- .../commands/DeleteAllExamplesCommand.ts | 4 ++++ .../commands/ResetAllExamplesCommand.ts | 4 ++++ .../commands/ShowAllExamplesCommand.ts | 4 ++++ .../commands/ShowExamplesOnTopCommand.ts | 11 +++++++---- 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurmentsOnTopCommand.ts b/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurmentsOnTopCommand.ts index cba24af00d3..c4b9b6c8e12 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurmentsOnTopCommand.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurmentsOnTopCommand.ts @@ -22,8 +22,7 @@ export class ShowMeasurmentsOnTopCommand extends RenderTargetCommand { } public override get isEnabled(): boolean { - const domainObject = this.rootDomainObject.getDescendantByType(MeasureDomainObject); - return domainObject !== undefined; + return this.getFirst() !== undefined; } public override get isChecked(): boolean { @@ -44,12 +43,16 @@ export class ShowMeasurmentsOnTopCommand extends RenderTargetCommand { // INSTANCE METHODS // ================================================== - public getDepthTest(): boolean { - const domainObject = this.rootDomainObject.getDescendantByType(MeasureDomainObject); + private getDepthTest(): boolean { + const domainObject = this.getFirst(); if (domainObject === undefined) { return false; } const style = domainObject.renderStyle; return style.depthTest; } + + private getFirst(): MeasureDomainObject | undefined { + return this.rootDomainObject.getDescendantByType(MeasureDomainObject); + } } diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts index d2ab8ffda47..d5881c34011 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts @@ -34,6 +34,10 @@ export class DeleteAllExamplesCommand extends RenderTargetCommand { return true; } + // ================================================== + // INSTANCE METHODS + // ================================================== + private getFirst(): ExampleDomainObject | undefined { return this.rootDomainObject.getDescendantByType(ExampleDomainObject); } diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts index 9279f8ddf5f..9ce568a2209 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts @@ -33,6 +33,10 @@ export class ResetAllExamplesCommand extends RenderTargetCommand { return true; } + // ================================================== + // INSTANCE METHODS + // ================================================== + private getFirst(): ExampleDomainObject | undefined { return this.rootDomainObject.getDescendantByType(ExampleDomainObject); } diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts index 9a16e75b228..36568a02eb2 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts @@ -40,6 +40,10 @@ export class ShowAllExamplesCommand extends RenderTargetCommand { return true; } + // ================================================== + // INSTANCE METHODS + // ================================================== + private getFirst(): ExampleDomainObject | undefined { return this.rootDomainObject.getDescendantByType(ExampleDomainObject); } diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts index ba888566b30..7ed28fdc064 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts @@ -22,8 +22,7 @@ export class ShowExamplesOnTopCommand extends RenderTargetCommand { } public override get isEnabled(): boolean { - const domainObject = this.rootDomainObject.getDescendantByType(ExampleDomainObject); - return domainObject !== undefined; + return this.getFirst() !== undefined; } public override get isChecked(): boolean { @@ -44,11 +43,15 @@ export class ShowExamplesOnTopCommand extends RenderTargetCommand { // INSTANCE METHODS // ================================================== - public getDepthTest(): boolean { - const domainObject = this.rootDomainObject.getDescendantByType(ExampleDomainObject); + private getDepthTest(): boolean { + const domainObject = this.getFirst(); if (domainObject === undefined) { return false; } return domainObject.renderStyle.depthTest; } + + private getFirst(): ExampleDomainObject | undefined { + return this.rootDomainObject.getDescendantByType(ExampleDomainObject); + } } From 6eb41eec31d23921fca378fc539b729fe7fc8416 Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Sun, 9 Jun 2024 20:51:26 +0200 Subject: [PATCH 3/6] A lot smaller changes --- .../architecture/base/commands/BaseCommand.ts | 6 +- .../base/commands/BaseEditTool.ts | 53 +- .../architecture/base/commands/BaseTool.ts | 5 +- .../base/commands/NavigationTool.ts | 3 +- .../CopyToClipboardCommand.ts | 1 - .../DeleteDomainObjectCommand.ts | 5 +- ...Command.ts => ToggleMetricUnitsCommand.ts} | 5 +- .../base/domainObjects/DomainObject.ts | 35 +- .../base/domainObjects/VisualDomainObject.ts | 2 +- .../base/domainObjectsHelpers/BaseCreator.ts | 6 +- .../base/domainObjectsHelpers/BaseDragger.ts | 4 +- .../DomainObjectChange.ts | 28 +- .../DomainObjectIntersection.ts | 2 +- .../base/domainObjectsHelpers/FocusType.ts | 2 +- .../base/domainObjectsHelpers/PopupStyle.ts | 1 - .../base/domainObjectsHelpers/VisibleState.ts | 2 +- .../base/reactUpdaters/ActiveToolUpdater.ts | 2 +- .../base/utilities/box/BoxFace.ts | 2 +- .../architecture/base/views/GroupThreeView.ts | 6 +- .../src/architecture/base/views/ThreeView.ts | 8 +- .../concrete/axis/SetAxisVisibleCommand.ts | 1 - .../boxDomainObject/MeasureBoxCreator.ts | 2 +- .../boxDomainObject/MeasureBoxDragger.ts | 2 +- .../boxDomainObject/MeasureLineCreator.ts | 2 +- .../boxDomainObject/MeasureRenderStyle.ts | 2 +- .../boxDomainObject/MeasurementTool.ts | 36 +- .../boxDomainObject/SetCropBoxCommand.ts | 1 - ...ommand.ts => SetMeasurementTypeCommand.ts} | 5 +- ...and.ts => ShowMeasurementsOnTopCommand.ts} | 3 +- .../concrete/config/StoryBookConfig.ts | 8 + .../ExampleDomainObject.ts | 6 +- .../exampleDomainObject/ExampleDragger.ts | 6 +- .../exampleDomainObject/ExampleTool.ts | 11 +- .../exampleDomainObject/ExampleView.ts | 6 +- .../concrete/exampleDomainObject/README.md | 606 ++++++++++++++++++ .../commands/DeleteAllExamplesCommand.ts | 5 +- .../commands/ResetAllExamplesCommand.ts | 5 +- .../commands/ShowAllExamplesCommand.ts | 1 - .../commands/ShowExamplesOnTopCommand.ts | 3 +- .../SetTerrainVisibleCommand.ts | 1 - .../terrainDomainObject/TerrainThreeView.ts | 2 +- .../UpdateTerrainCommand.ts | 1 - .../components/Architecture/CommandButton.tsx | 7 +- .../stories/Architecture.stories.tsx | 78 +-- 44 files changed, 793 insertions(+), 185 deletions(-) rename react-components/src/architecture/base/concreteCommands/{ToogleMetricUnitsCommand.ts => ToggleMetricUnitsCommand.ts} (82%) rename react-components/src/architecture/concrete/boxDomainObject/{SetMeasurmentTypeCommand.ts => SetMeasurementTypeCommand.ts} (91%) rename react-components/src/architecture/concrete/boxDomainObject/{ShowMeasurmentsOnTopCommand.ts => ShowMeasurementsOnTopCommand.ts} (91%) create mode 100644 react-components/src/architecture/concrete/exampleDomainObject/README.md diff --git a/react-components/src/architecture/base/commands/BaseCommand.ts b/react-components/src/architecture/base/commands/BaseCommand.ts index a01bd891b0d..d3fe91c0ff7 100644 --- a/react-components/src/architecture/base/commands/BaseCommand.ts +++ b/react-components/src/architecture/base/commands/BaseCommand.ts @@ -8,7 +8,7 @@ import { clear, remove } from '../utilities/extensions/arrayExtensions'; type UpdateDelegate = (command: BaseCommand) => void; /** - * Base class for all command and tools. Thses are object that can do a + * Base class for all command and tools. These are object that can do a * user interaction with the system. It also have enough information to * generate the UI for the command. */ @@ -55,6 +55,10 @@ export abstract class BaseCommand { return 'Unknown'; } + public get buttonType(): string { + return 'ghost'; + } + public get isEnabled(): boolean { return true; } diff --git a/react-components/src/architecture/base/commands/BaseEditTool.ts b/react-components/src/architecture/base/commands/BaseEditTool.ts index 5cdec1a3945..34ea748fc63 100644 --- a/react-components/src/architecture/base/commands/BaseEditTool.ts +++ b/react-components/src/architecture/base/commands/BaseEditTool.ts @@ -13,7 +13,7 @@ import { type AnyIntersection, CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/rev * The `BaseEditTool` class is an abstract class that extends the `NavigationTool` class. * It provides a base implementation for editing tools in a specific architecture. * Custom editing tools can be created by extending this class and overriding its methods. - * This class will also proivide the dragging functionality if the picked domain object has + * This class will also provide the dragging functionality if the picked domain object has * createDragger() overridden. */ export abstract class BaseEditTool extends NavigationTool { @@ -76,8 +76,8 @@ export abstract class BaseEditTool extends NavigationTool { /** * Determines whether the specified domain object can be selected or dragged by this edit tool. - * - * @param _domainObject - The domain object to be accepted. + * Override this function of the selection mechanism should be used. + * @param domainObject - The domain object to be accepted. * @returns `true` if the domain object can be accepted, `false` otherwise. */ protected canBeSelected(_domainObject: DomainObject): boolean { @@ -113,6 +113,11 @@ export abstract class BaseEditTool extends NavigationTool { // INSTANCE METHODS // ================================================== + /** + * Deselects all visual domain objects except for the specified object. + * If no object is specified, all visual domain objects will be deselected. + * @param except - The visual domain object to exclude from deselection. + */ protected deselectAll(except?: VisualDomainObject | undefined): void { const { rootDomainObject } = this; for (const domainObject of rootDomainObject.getDescendants()) { @@ -126,6 +131,48 @@ export abstract class BaseEditTool extends NavigationTool { } } + /** + * Retrieves all selected domain objects. + * Use only if multi selection is expected. + * @returns A generator that yields each selected domain object. + */ + protected *getAllSelected(): Generator { + const { rootDomainObject } = this; + for (const domainObject of rootDomainObject.getDescendants()) { + if (!domainObject.isSelected) { + continue; + } + if (!this.canBeSelected(domainObject)) { + continue; + } + yield domainObject; + } + } + + /** + * Retrieves the selected DomainObject. + * Use only if single selection is expected. + * @returns The selected DomainObject, or undefined if no object is selected. + */ + protected getSelected(): DomainObject | undefined { + const { rootDomainObject } = this; + for (const domainObject of rootDomainObject.getDescendants()) { + if (!domainObject.isSelected) { + continue; + } + if (!this.canBeSelected(domainObject)) { + continue; + } + return domainObject; + } + return undefined; + } + + /** + * Retrieves the intersected visual domain object from the given intersection. + * @param intersection - The intersection to retrieve the domain object from. + * @returns The intersected visual domain object, or undefined if no valid domain object is found. + */ protected getIntersectedDomainObject( intersection: AnyIntersection | undefined ): VisualDomainObject | undefined { diff --git a/react-components/src/architecture/base/commands/BaseTool.ts b/react-components/src/architecture/base/commands/BaseTool.ts index 11b9b8ce8ed..8713f52f1fa 100644 --- a/react-components/src/architecture/base/commands/BaseTool.ts +++ b/react-components/src/architecture/base/commands/BaseTool.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { type Ray, Raycaster, type Vector2 } from 'three'; @@ -24,7 +23,7 @@ import { PopupStyle } from '../domainObjectsHelpers/PopupStyle'; import { ThreeView } from '../views/ThreeView'; /** - * Base class for intraction in the 3D viewer + * Base class for interactions in the 3D viewer * Provides common functionality and virtual methods to be overridden by derived classes. */ export abstract class BaseTool extends RenderTargetCommand { @@ -58,7 +57,7 @@ export abstract class BaseTool extends RenderTargetCommand { } public getToolbarStyle(): PopupStyle { - // Override this to pclase the extra separate toolbar + // Override this to place the the toolbar // Default lower left corner return new PopupStyle({ bottom: 0, left: 0 }); } diff --git a/react-components/src/architecture/base/commands/NavigationTool.ts b/react-components/src/architecture/base/commands/NavigationTool.ts index d3bc621240c..03db99dee25 100644 --- a/react-components/src/architecture/base/commands/NavigationTool.ts +++ b/react-components/src/architecture/base/commands/NavigationTool.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { BaseTool } from './BaseTool'; @@ -13,7 +12,7 @@ import { type TranslateKey } from '../utilities/TranslateKey'; */ export class NavigationTool extends BaseTool { // ================================================== - // INSTANVE PROPERTIES + // INSTANCE PROPERTIES // ================================================== private get cameraManager(): IFlexibleCameraManager { diff --git a/react-components/src/architecture/base/concreteCommands/CopyToClipboardCommand.ts b/react-components/src/architecture/base/concreteCommands/CopyToClipboardCommand.ts index 430f25e6274..8ba7a9610e2 100644 --- a/react-components/src/architecture/base/concreteCommands/CopyToClipboardCommand.ts +++ b/react-components/src/architecture/base/concreteCommands/CopyToClipboardCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { BaseCommand } from '../commands/BaseCommand'; diff --git a/react-components/src/architecture/base/concreteCommands/DeleteDomainObjectCommand.ts b/react-components/src/architecture/base/concreteCommands/DeleteDomainObjectCommand.ts index 76293d36f48..730c5e194f2 100644 --- a/react-components/src/architecture/base/concreteCommands/DeleteDomainObjectCommand.ts +++ b/react-components/src/architecture/base/concreteCommands/DeleteDomainObjectCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { BaseCommand } from '../commands/BaseCommand'; @@ -25,6 +24,10 @@ export class DeleteDomainObjectCommand extends BaseCommand { return 'Delete'; } + public override get buttonType(): string { + return 'ghost-destructive'; + } + public override get isEnabled(): boolean { return this._domainObject !== undefined && this._domainObject.canBeRemoved; } diff --git a/react-components/src/architecture/base/concreteCommands/ToogleMetricUnitsCommand.ts b/react-components/src/architecture/base/concreteCommands/ToggleMetricUnitsCommand.ts similarity index 82% rename from react-components/src/architecture/base/concreteCommands/ToogleMetricUnitsCommand.ts rename to react-components/src/architecture/base/concreteCommands/ToggleMetricUnitsCommand.ts index 531a2035bc9..a3862f5e045 100644 --- a/react-components/src/architecture/base/concreteCommands/ToogleMetricUnitsCommand.ts +++ b/react-components/src/architecture/base/concreteCommands/ToggleMetricUnitsCommand.ts @@ -1,13 +1,12 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../commands/RenderTargetCommand'; import { Changes } from '../domainObjectsHelpers/Changes'; import { type TranslateKey } from '../utilities/TranslateKey'; -export class ToogleMetricUnitsCommand extends RenderTargetCommand { +export class ToggleMetricUnitsCommand extends RenderTargetCommand { // ================================================== // OVERRIDES of BaseCommand // ================================================== @@ -29,7 +28,7 @@ export class ToogleMetricUnitsCommand extends RenderTargetCommand { const { renderTarget } = this; const unitSystem = renderTarget.rootDomainObject.unitSystem; unitSystem.isMetric = !unitSystem.isMetric; - renderTarget.rootDomainObject.notifyRecursive(Changes.unit); + renderTarget.rootDomainObject.notifyDescendants(Changes.unit); return true; } } diff --git a/react-components/src/architecture/base/domainObjects/DomainObject.ts b/react-components/src/architecture/base/domainObjects/DomainObject.ts index 484cb58c1f2..83b49801b09 100644 --- a/react-components/src/architecture/base/domainObjects/DomainObject.ts +++ b/react-components/src/architecture/base/domainObjects/DomainObject.ts @@ -3,6 +3,7 @@ */ import { type Color } from 'three'; +import { BLACK_COLOR, WHITE_COLOR } from '../utilities/colors/colorExtensions'; import { type RenderStyle } from '../domainObjectsHelpers/RenderStyle'; import { DomainObjectChange } from '../domainObjectsHelpers/DomainObjectChange'; import { Changes } from '../domainObjectsHelpers/Changes'; @@ -13,7 +14,6 @@ import { clear, removeAt } from '../utilities/extensions/arrayExtensions'; import { getNextColor } from '../utilities/colors/getNextColor'; import { type RevealRenderTarget } from '../renderTarget/RevealRenderTarget'; import { ColorType } from '../domainObjectsHelpers/ColorType'; -import { BLACK_COLOR, WHITE_COLOR } from '../utilities/colors/colorExtensions'; import { Views } from '../domainObjectsHelpers/Views'; import { type PanelInfo } from '../domainObjectsHelpers/PanelInfo'; import { PopupStyle } from '../domainObjectsHelpers/PopupStyle'; @@ -42,7 +42,7 @@ export abstract class DomainObject { // For instance you can have many crop boxes, but only one can be used at the time. private _isActive: boolean = false; - // Expaned when it is shown in a tree view + // Expand when it is shown in a tree view private _isExpanded = false; // Parent-Child relationship @@ -74,7 +74,7 @@ export abstract class DomainObject { } // ================================================== - // INSTANCE/VIRTUAL METHODS: Nameing + // INSTANCE/VIRTUAL METHODS: Naming // ================================================== public get canChangeName(): boolean { @@ -246,6 +246,15 @@ export abstract class DomainObject { protected notifyCore(change: DomainObjectChange): void { this.views.notify(this, change); + // If isRenderStyleRoot is true, notify all descendants + if (change.isChanged(Changes.renderStyle) && this.isRenderStyleRoot) { + const description = change.getChangedDescription(Changes.renderStyle); + if (description !== undefined) { + const renderStyleChange = new DomainObjectChange(description); + this.notifyDescendants(renderStyleChange); + } + } + if ( change.isChanged( Changes.visibleState, @@ -309,13 +318,21 @@ export abstract class DomainObject { /** * Override if the render style is taken from another domain object, for instance the parent - * or somewhere else in the hieracy + * or somewhere else in the hierarchy * @returns The render style root */ public get renderStyleRoot(): DomainObject | undefined { return undefined; } + /** + * Override if this domain object is a render style root, se method above + * @returns true if this is a render style root + */ + public get isRenderStyleRoot(): boolean { + return false; + } + /** * Factory method to create the render style for the domain object. * Override this method to create a custom render style. @@ -328,7 +345,7 @@ export abstract class DomainObject { /** * Verifies the render style for the domain object, because the render style may * be not valid in some cases. In this method you can change the render style. - * You can also change som fields in the rebnderstyle to get default values + * You can also change som fields in the renderStyle to get default values * dependent of the domain object itself. * Override this method when needed */ @@ -378,7 +395,7 @@ export abstract class DomainObject { public setVisibleInteractive( visible: boolean, renderTarget: RevealRenderTarget, - topLevel = true // When calling this from outside, this value should alwaus be true + topLevel = true // When calling this from outside, this value should always be true ): boolean { const visibleState = this.getVisibleState(renderTarget); if (visibleState === VisibleState.Disabled) { @@ -413,7 +430,7 @@ export abstract class DomainObject { this.notifyCore(change); } - public notifyRecursive(change: DomainObjectChange | symbol): void { + public notifyDescendants(change: DomainObjectChange | symbol): void { if (!(change instanceof DomainObjectChange)) { change = new DomainObjectChange(change); } @@ -464,7 +481,7 @@ export abstract class DomainObject { } public get root(): DomainObject { - // Returns the root of the hierarcy, regardless what it is + // Returns the root of the hierarchy, regardless what it is return this.parent === undefined ? this : this.parent.root; } @@ -776,7 +793,7 @@ export abstract class DomainObject { // ================================================== // INSTANCE METHODS: Color type - // Used in the renderstyle to determin which of the color a domain object should have. + // Used in the render style to determine which of the color a domain object should have. // ================================================== public supportsColorType(colorType: ColorType, solid: boolean): boolean { diff --git a/react-components/src/architecture/base/domainObjects/VisualDomainObject.ts b/react-components/src/architecture/base/domainObjects/VisualDomainObject.ts index 720e0da4006..545a6f627eb 100644 --- a/react-components/src/architecture/base/domainObjects/VisualDomainObject.ts +++ b/react-components/src/architecture/base/domainObjects/VisualDomainObject.ts @@ -61,7 +61,7 @@ export abstract class VisualDomainObject extends DomainObject { /** * Determines whether the visual domain object can create a three view. - * It may have a state when it can not create a view bacause of other dependencies + * It may have a state when it can not create a view because of other dependencies * * @returns A boolean value indicating whether the visual domain object can create a three view. */ diff --git a/react-components/src/architecture/base/domainObjectsHelpers/BaseCreator.ts b/react-components/src/architecture/base/domainObjectsHelpers/BaseCreator.ts index 0735a332859..fb0d5e66c4a 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/BaseCreator.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/BaseCreator.ts @@ -55,9 +55,9 @@ export abstract class BaseCreator { // ================================================== /** - * Gets the value indicating whether to prefer intersection with somthing. + * Gets the value indicating whether to prefer intersection with something. * If this is true, it will first try to intersect an object. If false the point - * will normally be calculatd based on the previous point and the ray in addPointCore + * will normally be calculated based on the previous point and the ray in addPointCore * * @returns {boolean} The value indicating whether to prefer intersection. */ @@ -73,7 +73,7 @@ export abstract class BaseCreator { protected abstract get minimumPointCount(): number; /** - * @returns The maximim required points to create the domain object. + * @returns The maximum required points to create the domain object. */ protected abstract get maximumPointCount(): number; diff --git a/react-components/src/architecture/base/domainObjectsHelpers/BaseDragger.ts b/react-components/src/architecture/base/domainObjectsHelpers/BaseDragger.ts index b92d6c373cd..0f8a904375e 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/BaseDragger.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/BaseDragger.ts @@ -21,7 +21,7 @@ export abstract class BaseDragger { protected readonly ray: Ray = new Ray(); // Intersection point at pointer down in CDF coordinates // ================================================== - // CONTRUCTOR + // CONSTRUCTOR // ================================================== /** * Represents a base dragger object. @@ -50,7 +50,7 @@ export abstract class BaseDragger { /** * Called every times the mouse moves during dragging * @param event - The pointer event. - * @param ray - The current ray in CDF cooordinates + * @param ray - The current ray in CDF coordinates * @returns True if the dragger has changed the domain object, false otherwise. */ public abstract onPointerDrag(event: PointerEvent, ray: Ray): boolean; diff --git a/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts b/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts index 73c5e1f69e6..31f25b49ff9 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts @@ -15,9 +15,12 @@ export class DomainObjectChange { // CONSTRUCTOR // ================================================== - public constructor(change?: symbol, fieldName?: string) { - if (change !== undefined) { - this.add(change, fieldName); + public constructor(change?: symbol | ChangedDescription, fieldName?: string) { + if (typeof change === 'symbol') { + this.addChange(change, fieldName); + } + if (change instanceof ChangedDescription) { + this.addChangedDescription(change); } } @@ -47,17 +50,17 @@ export class DomainObjectChange { } /** - * Checks if the domain object has been changed based on the specified change and some specific fieldnames. + * Checks if the domain object has been changed based on the specified change and some specific field names. * For instance * if (isFieldNameChanged(Changes.renderStyle, 'lineWidth', 'lineColor')) { - * // Now you have to update the line matrial only + * // Now you have to update the line material only * } * @param change - The symbol representing the change. * @param fieldNames - The field names to compare against the change symbol. * @returns A boolean indicating whether the name has changed or not. */ public isFieldNameChanged(change: symbol, ...fieldNames: string[]): boolean { - // This igonores space and case. + // This ignores space and case. const fieldName = this.getFieldNameBySymbol(change); if (fieldName === undefined) { return false; @@ -74,7 +77,7 @@ export class DomainObjectChange { // INSTANCE METHODS: Getters // ================================================== - private getChangedDescription(change: symbol): ChangedDescription | undefined { + public getChangedDescription(change: symbol): ChangedDescription | undefined { if (this._changes === undefined) { return undefined; } @@ -90,14 +93,17 @@ export class DomainObjectChange { // INSTANCE METHODS: Operations // ================================================== - public add(change: symbol, fieldName?: string): void { - if (change === undefined) { - return; + public addChange(change: symbol, fieldName?: string): void { + if (change !== undefined) { + this.addChangedDescription(new ChangedDescription(change, fieldName)); } + } + + public addChangedDescription(changedDescription: ChangedDescription): void { if (this._changes === undefined) { this._changes = []; } - this._changes.push(new ChangedDescription(change, fieldName)); + this._changes.push(changedDescription); } } diff --git a/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectIntersection.ts b/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectIntersection.ts index 05a7a6af5e9..0f5deff05c0 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectIntersection.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectIntersection.ts @@ -6,7 +6,7 @@ import { type CustomObjectIntersection, type AnyIntersection } from '@cognite/re import { type DomainObject } from '../domainObjects/DomainObject'; // DomainObjectIntersection extends the CustomObjectIntersection with a domainObject property. -// This had been a lot simpler with object orienteted intersection objects, but Reveal has of some +// This had been a lot simpler with object orientated intersection objects, but Reveal has of some // unknown reason used types here - to make it hard for us to extend them. // It is working but I don't like it at all. diff --git a/react-components/src/architecture/base/domainObjectsHelpers/FocusType.ts b/react-components/src/architecture/base/domainObjectsHelpers/FocusType.ts index 95cf5dda053..1bece000aaf 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/FocusType.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/FocusType.ts @@ -4,7 +4,7 @@ export enum FocusType { None, - Focus, // Focus not any particalar place + Focus, // Focus not any particular place Face, // Focus on the face Corner, // Focus on a corner Rotation, // Focus at rotation diff --git a/react-components/src/architecture/base/domainObjectsHelpers/PopupStyle.ts b/react-components/src/architecture/base/domainObjectsHelpers/PopupStyle.ts index 8e2c13f37d1..369eb5303cb 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/PopupStyle.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/PopupStyle.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ type PopupProps = { diff --git a/react-components/src/architecture/base/domainObjectsHelpers/VisibleState.ts b/react-components/src/architecture/base/domainObjectsHelpers/VisibleState.ts index 05ea53712b3..431418b1b39 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/VisibleState.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/VisibleState.ts @@ -7,5 +7,5 @@ export enum VisibleState { Some, // Partly visible None, // None visible CanNotBeVisibleNow, // Can not be checked on, but it can be visible - Disabled // Visiable disabled + Disabled // Visible disabled } diff --git a/react-components/src/architecture/base/reactUpdaters/ActiveToolUpdater.ts b/react-components/src/architecture/base/reactUpdaters/ActiveToolUpdater.ts index 01ec6f3d1bf..0dc995e1060 100644 --- a/react-components/src/architecture/base/reactUpdaters/ActiveToolUpdater.ts +++ b/react-components/src/architecture/base/reactUpdaters/ActiveToolUpdater.ts @@ -23,7 +23,7 @@ export class ActiveToolUpdater { public static update(): void { // Increment the counter, so the state change in React and force a redraw each time the active tool changes // The reason for solution it that I only want to store the active tool at one single location, since this gives a more - // stabel code, and never goes out of sync. + // stable code, and never goes out of sync. // React get the active tool by: renderTarget.commandsController.activeTool; if (this._setCounter === undefined) { return; diff --git a/react-components/src/architecture/base/utilities/box/BoxFace.ts b/react-components/src/architecture/base/utilities/box/BoxFace.ts index 44f2139947a..a4c13bb6575 100644 --- a/react-components/src/architecture/base/utilities/box/BoxFace.ts +++ b/react-components/src/architecture/base/utilities/box/BoxFace.ts @@ -12,7 +12,7 @@ export class BoxFace { private _face: number = 0; // ================================================== - // CONTRUCTOR + // CONSTRUCTOR // ================================================== public constructor(face: number = 0) { diff --git a/react-components/src/architecture/base/views/GroupThreeView.ts b/react-components/src/architecture/base/views/GroupThreeView.ts index 49437b948cd..87345ad232e 100644 --- a/react-components/src/architecture/base/views/GroupThreeView.ts +++ b/react-components/src/architecture/base/views/GroupThreeView.ts @@ -45,7 +45,7 @@ export abstract class GroupThreeView extends ThreeView implements ICustomObject this.removeChildren(); } if (this.isEmpty) { - this.makeChilderen(); + this.makeChildren(); } return this._group; } @@ -110,7 +110,7 @@ export abstract class GroupThreeView extends ThreeView implements ICustomObject public override initialize(): void { super.initialize(); if (this.isEmpty) { - this.makeChilderen(); + this.makeChildren(); } const { viewer } = this.renderTarget; viewer.addCustomObject(this); @@ -184,7 +184,7 @@ export abstract class GroupThreeView extends ThreeView implements ICustomObject // INSTANCE METHODS // ================================================== - private makeChilderen(): void { + private makeChildren(): void { if (!this.isEmpty) { throw Error('Can make the object when it is already made'); } diff --git a/react-components/src/architecture/base/views/ThreeView.ts b/react-components/src/architecture/base/views/ThreeView.ts index 600e7a61e1b..5078d7b02e4 100644 --- a/react-components/src/architecture/base/views/ThreeView.ts +++ b/react-components/src/architecture/base/views/ThreeView.ts @@ -12,10 +12,10 @@ import { type PerspectiveCamera, type Box3 } from 'three'; /** * Represents an abstract base class for a Three.js view in the application. - * Extends the `BaseView` class. It only has the poiinter to the renderTarget and a bounding box. - * Inirit from this class if you want some visualization that do dot require a group object3D as the root object. - * It can for instance be a view that chages somting 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 develper. + * Extends the `BaseView` class. It only has the pointer to the renderTarget and a bounding box. + * Inherit from this class if you want some visualization that do dot require a group object3D as the root object. + * 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 { // ================================================== diff --git a/react-components/src/architecture/concrete/axis/SetAxisVisibleCommand.ts b/react-components/src/architecture/concrete/axis/SetAxisVisibleCommand.ts index 4e15bc94b67..a85e4dfc38c 100644 --- a/react-components/src/architecture/concrete/axis/SetAxisVisibleCommand.ts +++ b/react-components/src/architecture/concrete/axis/SetAxisVisibleCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts index 001ec405c29..699126cc9c1 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts @@ -29,7 +29,7 @@ export class MeasureBoxCreator extends BaseCreator { private readonly _domainObject: MeasureBoxDomainObject; // ================================================== - // CONTRUCTOR + // CONSTRUCTOR // ================================================== constructor(measureType: MeasureType) { diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDragger.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDragger.ts index 8724e5f5873..e570d6481d2 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDragger.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDragger.ts @@ -56,7 +56,7 @@ export class MeasureBoxDragger extends BaseDragger { } // ================================================== - // CONTRUCTOR + // CONSTRUCTOR // ================================================== public constructor(props: CreateDraggerProps, domainObject: MeasureBoxDomainObject) { diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineCreator.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineCreator.ts index 2179eec0fab..5c00b7669bc 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineCreator.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineCreator.ts @@ -22,7 +22,7 @@ export class MeasureLineCreator extends BaseCreator { private readonly _domainObject: MeasureLineDomainObject; // ================================================== - // CONTRUCTOR + // CONSTRUCTOR // ================================================== constructor(measureType: MeasureType) { diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureRenderStyle.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureRenderStyle.ts index 53a499bf8fe..5fd0bab3f0e 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureRenderStyle.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureRenderStyle.ts @@ -17,5 +17,5 @@ export abstract class MeasureRenderStyle extends RenderStyle { public textColor = WHITE_COLOR.clone(); public textBgColor = new Color('#232323'); public textOpacity = 0.9; - public relativeTextSize = 0.05; // Relative to diagonal of the measurment object for box and average of lenght of line segments for line + public relativeTextSize = 0.05; // Relative to diagonal of the measurement object for box and average of lenght of line segments for line } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts index 23e0a41c104..4ebeee6c2ff 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts @@ -18,13 +18,13 @@ import { MeasureLineDomainObject } from './MeasureLineDomainObject'; import { MeasureRenderStyle } from './MeasureRenderStyle'; import { type DomainObject } from '../../base/domainObjects/DomainObject'; import { MeasureDomainObject } from './MeasureDomainObject'; -import { ShowMeasurmentsOnTopCommand } from './ShowMeasurmentsOnTopCommand'; -import { SetMeasurmentTypeCommand } from './SetMeasurmentTypeCommand'; +import { ShowMeasurementsOnTopCommand } from './ShowMeasurementsOnTopCommand'; +import { SetMeasurementTypeCommand } from './SetMeasurementTypeCommand'; import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; import { type RootDomainObject } from '../../base/domainObjects/RootDomainObject'; import { CommandsUpdater } from '../../base/reactUpdaters/CommandsUpdater'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; -import { ToogleMetricUnitsCommand } from '../../base/concreteCommands/ToogleMetricUnitsCommand'; +import { ToggleMetricUnitsCommand } from '../../base/concreteCommands/ToggleMetricUnitsCommand'; export class MeasurementTool extends BaseEditTool { // ================================================== @@ -48,15 +48,15 @@ export class MeasurementTool extends BaseEditTool { public override getToolbar(): Array { return [ - new SetMeasurmentTypeCommand(MeasureType.Line), - new SetMeasurmentTypeCommand(MeasureType.Polyline), - new SetMeasurmentTypeCommand(MeasureType.Polygon), - new SetMeasurmentTypeCommand(MeasureType.HorizontalArea), - new SetMeasurmentTypeCommand(MeasureType.VerticalArea), - new SetMeasurmentTypeCommand(MeasureType.Volume), + new SetMeasurementTypeCommand(MeasureType.Line), + new SetMeasurementTypeCommand(MeasureType.Polyline), + new SetMeasurementTypeCommand(MeasureType.Polygon), + new SetMeasurementTypeCommand(MeasureType.HorizontalArea), + new SetMeasurementTypeCommand(MeasureType.VerticalArea), + new SetMeasurementTypeCommand(MeasureType.Volume), undefined, // Separator - new ToogleMetricUnitsCommand(), - new ShowMeasurmentsOnTopCommand() + new ToggleMetricUnitsCommand(), + new ShowMeasurementsOnTopCommand() ]; } @@ -86,7 +86,7 @@ export class MeasurementTool extends BaseEditTool { public override onKey(event: KeyboardEvent, down: boolean): void { if (down && event.key === 'Delete') { - const domainObject = this.rootDomainObject.getSelectedDescendantByType(MeasureDomainObject); + const domainObject = this.getSelected(); if (domainObject !== undefined) { domainObject.removeInteractive(); } @@ -186,10 +186,10 @@ export class MeasurementTool extends BaseEditTool { // Click in the "air" return; } - const measurment = this.getMeasurement(intersection); - if (measurment !== undefined) { - this.deselectAll(measurment); - measurment.setSelectedInteractive(true); + const measurement = this.getMeasurement(intersection); + if (measurement !== undefined) { + this.deselectAll(measurement); + measurement.setSelectedInteractive(true); return; } const ray = this.getRay(event); @@ -219,7 +219,7 @@ export class MeasurementTool extends BaseEditTool { public override async onPointerDown(event: PointerEvent, leftButton: boolean): Promise { if (this._creator !== undefined) { - return; // Prevent draggin while creating the new + return; // Prevent dragging while creating the new } await super.onPointerDown(event, leftButton); } @@ -241,7 +241,7 @@ export class MeasurementTool extends BaseEditTool { return; } if (this._creator.handleEscape()) { - // Sucessfully created, set it back to none + // Successfully created, set it back to none this.measureType = MeasureType.None; CommandsUpdater.update(this.renderTarget); } diff --git a/react-components/src/architecture/concrete/boxDomainObject/SetCropBoxCommand.ts b/react-components/src/architecture/concrete/boxDomainObject/SetCropBoxCommand.ts index f2de2d5eb85..3cb948b77ac 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/SetCropBoxCommand.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/SetCropBoxCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; diff --git a/react-components/src/architecture/concrete/boxDomainObject/SetMeasurmentTypeCommand.ts b/react-components/src/architecture/concrete/boxDomainObject/SetMeasurementTypeCommand.ts similarity index 91% rename from react-components/src/architecture/concrete/boxDomainObject/SetMeasurmentTypeCommand.ts rename to react-components/src/architecture/concrete/boxDomainObject/SetMeasurementTypeCommand.ts index 5a91ddb3009..be21b504d02 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/SetMeasurmentTypeCommand.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/SetMeasurementTypeCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; @@ -9,7 +8,7 @@ import { MeasureType, getIconByMeasureType, getTooltipByMeasureType } from './Me import { MeasurementTool } from './MeasurementTool'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; -export class SetMeasurmentTypeCommand extends RenderTargetCommand { +export class SetMeasurementTypeCommand extends RenderTargetCommand { private readonly _measureType: MeasureType; // ================================================== @@ -61,7 +60,7 @@ export class SetMeasurmentTypeCommand extends RenderTargetCommand { } public override equals(other: BaseCommand): boolean { - if (!(other instanceof SetMeasurmentTypeCommand)) { + if (!(other instanceof SetMeasurementTypeCommand)) { return false; } return this._measureType === other._measureType; diff --git a/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurmentsOnTopCommand.ts b/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts similarity index 91% rename from react-components/src/architecture/concrete/boxDomainObject/ShowMeasurmentsOnTopCommand.ts rename to react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts index c4b9b6c8e12..741b6d17a92 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurmentsOnTopCommand.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; @@ -8,7 +7,7 @@ import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; import { MeasureDomainObject } from './MeasureDomainObject'; -export class ShowMeasurmentsOnTopCommand extends RenderTargetCommand { +export class ShowMeasurementsOnTopCommand extends RenderTargetCommand { // ================================================== // OVERRIDES // ================================================== diff --git a/react-components/src/architecture/concrete/config/StoryBookConfig.ts b/react-components/src/architecture/concrete/config/StoryBookConfig.ts index f3ef4361182..3bc83fb6b30 100644 --- a/react-components/src/architecture/concrete/config/StoryBookConfig.ts +++ b/react-components/src/architecture/concrete/config/StoryBookConfig.ts @@ -15,12 +15,19 @@ import { MeasurementTool } from '../boxDomainObject/MeasurementTool'; import { AxisGizmoTool } from '@cognite/reveal/tools'; import { BaseRevealConfig } from '../../base/renderTarget/BaseRevealConfig'; import { type RevealRenderTarget } from '../../base/renderTarget/RevealRenderTarget'; +import { NavigationTool } from '../../base/commands/NavigationTool'; +import { type BaseTool } from '../../base/commands/BaseTool'; +import { ToggleMetricUnitsCommand } from '../../base/concreteCommands/ToggleMetricUnitsCommand'; export class StoryBookConfig extends BaseRevealConfig { // ================================================== // OVERRIDES // ================================================== + public override createDefaultTool(): BaseTool { + return new NavigationTool(); + } + public override createMainToolbar(): Array { return [ new SetFlexibleControlsTypeCommand(FlexibleControlsType.Orbit), @@ -28,6 +35,7 @@ export class StoryBookConfig extends BaseRevealConfig { undefined, new FitViewCommand(), new SetAxisVisibleCommand(), + new ToggleMetricUnitsCommand(), undefined, new ExampleTool(), new MeasurementTool(), diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleDomainObject.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleDomainObject.ts index e26e4244c43..8842ab26e70 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleDomainObject.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleDomainObject.ts @@ -63,9 +63,9 @@ export class ExampleDomainObject extends VisualDomainObject { public override getPanelInfo(): PanelInfo | undefined { const info = new PanelInfo(); info.setHeader('NAME', this.name); - add('XCORDINATE', 'X coordinate', this.center.x, Quantity.Length); - add('YCORDINATE', 'Y coordinate', this.center.y, Quantity.Length); - add('ZCORDINATE', 'Z coordinate', this.center.z, Quantity.Length); + add('XCOORDINATE', 'X coordinate', this.center.x, Quantity.Length); + add('YCOORDINATE', 'Y coordinate', this.center.y, Quantity.Length); + add('ZCOORDINATE', 'Z coordinate', this.center.z, Quantity.Length); return info; function add(key: string, fallback: string, value: number, quantity: Quantity): void { diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleDragger.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleDragger.ts index 334617a94c8..202cea37d04 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleDragger.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleDragger.ts @@ -19,10 +19,10 @@ export class ExampleDragger extends BaseDragger { private readonly _domainObject: ExampleDomainObject; private readonly _center: Vector3; private readonly _plane: Plane; - private readonly _offset: Vector3; + private readonly _offset: Vector3; // Correction for picking the sphere other places than in the center // ================================================== - // CONTRUCTOR + // CONSTRUCTOR // ================================================== public constructor(props: CreateDraggerProps, domainObject: ExampleDomainObject) { @@ -31,7 +31,7 @@ export class ExampleDragger extends BaseDragger { this._center = this._domainObject.center.clone(); this._plane = new Plane().setFromNormalAndCoplanarPoint(this.ray.direction, this._center); - // This is the adjustment for hittig the sphere other places than in the center + // This is the correction for picking at the sphere other places than in the center const planeIntersection = this.ray.intersectPlane(this._plane, new Vector3()); if (planeIntersection === null) { throw new Error('Failed to intersect plane'); diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts index c3a284e697d..42a943a7b64 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleTool.ts @@ -15,7 +15,7 @@ import { type DomainObject } from '../../base/domainObjects/DomainObject'; import { type HSL } from 'three'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; import { ShowExamplesOnTopCommand } from './commands/ShowExamplesOnTopCommand'; -import { ToogleMetricUnitsCommand } from '../../base/concreteCommands/ToogleMetricUnitsCommand'; +import { DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; export class ExampleTool extends BaseEditTool { // ================================================== @@ -36,7 +36,7 @@ export class ExampleTool extends BaseEditTool { public override onKey(event: KeyboardEvent, down: boolean): void { if (down && event.key === 'Delete') { - const domainObject = this.rootDomainObject.getSelectedDescendantByType(ExampleDomainObject); + const domainObject = this.getSelected(); if (domainObject !== undefined) { domainObject.removeInteractive(); } @@ -63,13 +63,13 @@ export class ExampleTool extends BaseEditTool { // Change opacity const delta = Math.sign(event.deltaY) * 0.05; domainObject.renderStyle.opacity = clamp(domainObject.renderStyle.opacity + delta, 0.2, 1); - domainObject.notify(Changes.renderStyle); + domainObject.notify(new DomainObjectChange(Changes.renderStyle, 'opacity')); } else { // Change radius const factor = 1 - Math.sign(event.deltaY) * 0.1; domainObject.renderStyle.radius *= factor; + domainObject.notify(new DomainObjectChange(Changes.renderStyle, 'radius')); } - domainObject.notify(Changes.renderStyle); } public override async onHover(event: PointerEvent): Promise { @@ -116,8 +116,7 @@ export class ExampleTool extends BaseEditTool { new ResetAllExamplesCommand(), new ShowAllExamplesCommand(), new DeleteAllExamplesCommand(), - new ShowExamplesOnTopCommand(), - new ToogleMetricUnitsCommand() + new ShowExamplesOnTopCommand() ]; } diff --git a/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts b/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts index 4e1cfd8a3aa..de0780adda1 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/ExampleView.ts @@ -36,8 +36,7 @@ export class ExampleView extends GroupThreeView { public override update(change: DomainObjectChange): void { super.update(change); if (change.isChanged(Changes.selected, Changes.renderStyle, Changes.color)) { - this.removeChildren(); - this.invalidateBoundingBox(); + this.clearMemory(); this.invalidateRenderTarget(); } } @@ -53,10 +52,9 @@ export class ExampleView extends GroupThreeView { protected override addChildren(): void { const { domainObject, style } = this; - const color = domainObject.color; const geometry = new SphereGeometry(style.radius, 32, 16); const material = new MeshPhongMaterial({ - color, + color: domainObject.color, emissive: WHITE_COLOR, emissiveIntensity: domainObject.isSelected ? 0.4 : 0.0, shininess: 5, diff --git a/react-components/src/architecture/concrete/exampleDomainObject/README.md b/react-components/src/architecture/concrete/exampleDomainObject/README.md new file mode 100644 index 00000000000..42c1f3ad28c --- /dev/null +++ b/react-components/src/architecture/concrete/exampleDomainObject/README.md @@ -0,0 +1,606 @@ +# Introduction + +The architecture I will introduce has been developed since the 1990s and has evolved with me on different applications. It is a simple yet versatile architecture that can be used in systems with hundreds of developers and millions of lines of code. It scales well without the need to change the core code. + +**Reasons for using this architecture:** + +- Focuses on **what** to do rather than **how** +- Faster development +- Provides a good starting point +- Consistent patterns for similar features +- Reduces friction and frustrations within teams +- Facilitates faster onboarding of new team members + +**Basic concepts you will be introduced to:** + +- **Domain object:** Where the data resides. Domain objects are organized in a hierarchy. +- **Render style:** Defines how the data is visualized. +- **View:** The code responsible for rendering. +- **Command:** A user action that triggers a change. +- **Tool:** Special command with 3D viewer user actions. + +**The code can be divided in 3 parts:** + +- The architecture itself (should be stable after a while) +- The React part (should be stable after a while) +- **The business logic: This is the part you will develop.** + +**Limitations:** + +- It is limited when it comes to advanced React component. Everything that can not be generalized + must be done as usual. +- The Reveal objects, like CAD, Point clouds etc. still live somewhere inside Reveal and cannot be used as domain objects. +- Reveal is not a multi viewer system. This architecture support multi viewer, but cannot use it. +- I have not implemented any save and load data from CDF. This will come later. +- I have dropped several more advanced features in the architecture (for instance factories), because of the code review system we have. Unfortunate, in Cognite there is no culture for adding thing we may need in the future. + +Instead of sliders you will during the course (hopefully) be able to make functionality to create, manipulate and delete your own domain objects. I have build up the same object in the code myself and made it as good as possible. This is called `ExampleDomainObject` and should be used as a good and simple start point for similar and hopefully more advanced functionality. I will maintain and develop this further along with new concepts. + +# The exercises + +Now it is your turn. + +## Creating a domain object + +Decide a name of the domain object. You will create something that is simple and can be moved. In the rest a assume this is a point with some extension, but it can be anything that has a single color, can be scaled and move. Let the name end with `DomainObject`, then everybody will know what type it is. + +In the folder `src/architecture/concrete` make a new folder, with the name of your domain object. + +In the rest of the text I will call it a `PointDomainObject` and other objects accordingly, + +The this folder create the class `PointDomainObject`, extend it from `VisualDomainObject` since it should be visible. Make a field called `center`. Use Three.js primitives. A point is called `Vector3`. This point will be in CDF coordinated with Z up (I have assumed that in this example, and in my own implementation). + +You override these methods, so it has a icon and a type name. + +```typescript +public override get icon(): string { + return 'Circle'; +} + +public override get typeName(): string { + return 'Point'; +} +``` + +Use any icons you like on the https://zeroheight.com/37d494d9d/p/99acc1-system-icons + +> **ⓘ Try it out:** +> Compile it. At this moment you should be finished with your first version of the `PointDomainObject`. We will add more functionality it later. + +## Creating the style and the view. + +In order to visualize it, you have to create 2 classes. + +- `PointRenderStyle`, which is the parameters for visualization and should be extended from `RenderStyle`. +- `PointView`, which is the view itself, and should be extended from `GroupThreeView`. + +Put some fields into the `PointRenderStyle`: `radius`, `opacity` and `depthTest` (boolean). You may add others later. Note that the radius is a part of the style rather than the data. This is because a specific radius is just one way of showing the point. The data is the point in (x, y, z) coordinates itself. + +In the `PointView`, you can relay a lot of the the default implementation in `GroupThreeView`, but you have to create the visualization code yourself. This is done in the `addChildren` method. Here we add Three.js objects to the view. For those who are not familiar with Three.js, here is a suggested implementation: + +```typescript +protected override addChildren(): void { + const { domainObject, style } = this; + const geometry = new SphereGeometry(style.radius, 32, 16); + const material = new MeshPhongMaterial({ + color: domainObject.color, + emissive: WHITE_COLOR, + emissiveIntensity: domainObject.isSelected ? 0.4 : 0.0, + shininess: 5, + opacity: style.opacity, + transparent: true, + depthTest: style.depthTest + }); + const sphere = new Mesh(geometry, material); + const center = domainObject.center.clone(); + center.applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION); + sphere.position.copy(center); + this.addChild(sphere); +} +``` + +You may also override `intersectIfCloser` and `calculateBoundingBox`, but the base class uses the objects created by `addChildren` for the default implementation. But `intersectIfCloser` should often be overridden due to sloppy intersection algorithms in Three.js or if you for instance has labels which is not part of the 3D object itself. + +It is also nice to add some convenience properties to the view for reuse: + +```typescript +public override get domainObject(): PointDomainObject { + return super.domainObject as PointDomainObject; +} + +protected override get style(): PointRenderStyle { + return super.style as PointRenderStyle; +} +``` + +Then you have to tell the `PointDomainObject` which view and render style it should use. The you go back the the domain object implementation and override two methods, `createRenderStyle` and `createThreeView`. + +> **ⓘ Try it out:** At this moment you should be able to compile all 3 classes, `PointDomainObject`, `PointRenderStyle`and `PointView`. + +## Create the tool + +Here you implement the user interaction in the 3D viewer. Make a class called `PointTool` or something similar. The best is inherit from `NavigationTool`, which is the tool for all types of navigation. By doing this you will be able to navigate and manipulate the Point without going in and out of the `PointTool`. + +The framework let you only have one tool active at the same time. The default tool is `NavigateTool`, which is the basic camera navigation. By clicking at your `PointTool`, this will be active and the `NavigateTool` will be deactivated. You can have as many tools as you like. + +First you have to override 2 functions to get the tooltip and the icon on the button. Look in `BaseCommand` for `get icon` and `get tooltip`. `BaseCommand` is the base class of all the command and tools and let you override methods to be used by React. Use any icons you like on the https://zeroheight.com/37d494d9d/p/99acc1-system-icons. The tooltip should return a `TranslateKey`, which is ready for translation. + +Then you have to make some functionality. The simplest I can think about is to create your +`PointDomainObject` by clicking at something. Lets do that by overriding `onClick`. + +Here is the implementation. First find the intersection point, if not, use the default implementation, +then get the center of your point in CDF coordinates and create your domain object. Add it to the root and set it visible. + +```typescript + public override async onClick(event: PointerEvent): Promise { + const intersection = await this.getIntersection(event); + if (intersection === undefined) { + await super.onClick(event); + return; + } + const center = intersection.point.clone(); + const matrix = CDF_TO_VIEWER_TRANSFORMATION.clone().invert(); + center.applyMatrix4(matrix); + + const domainObject = new PointDomainObject(); + domainObject.center.copy(center); + + this.rootDomainObject.addChildInteractive(domainObject); + domainObject.setVisibleInteractive(true, this.renderTarget); + } +``` + +Note the method with suffix `Interactive`. These belong to a family of methods which is guarantee to update all necessary dependencies, including the user interface. They have according none interactive functions, which does, and are used to do things in batch and update in the end. + +We have also methods ending up with suffix `Core`. These are protected virtual method called by the +framework and should never been used directly. These rules make it easier to understand the code. + +Now, you could done this by Reveals `on('click',...)` functionality. In the following we will work more with the tool to make it like production quality. + +> **ⓘ Try it out:** At this moment you should be able to compile all 4 classes, `PointDomainObject`, `PointRenderStyle`, `PointView` and `PointTool`. + +## Run your application + +The only thing you need now it to tell the framework about your new tool. Go to the file `StoryBookConfig`. This file tells the framework how your application should to be. In `createMainToolbar` add your tool. + +> **ⓘ Try it out:** Compile and run. If everything works now, you will see your tool in the toolbar. Click on it to activate the tool. Click around in the viewer to create domain objects. + +Reveal itself is aware your domain object. To check that Reveal understand your new domain object you can take a quick check. + +> **ⓘ Try it out:** Make some points and deactivate your tool by unchecking it. Double click in one point. The camera should set the point in the middle of the screen and set the pivot point in the center (pivot point is center of rotation). Single click and the pivot point will be at the sphere of the point. This is according to the the camera behavior for the CAD model. + +## Implement selection functionality + +Instead of inherit from NavigationTool you should now inherit from BaseEditTool. This is also subclass of NavigationTool, but add some more functionality to the tool. To see how, do: + +Override the method `canBeSelected()` to return true when it is a `PointDomainObject` by using `instanceof`. + +Then, in your `onClick` method add this after the first if statement is done: + +```typescript +{ + const domainObject = this.getIntersectedDomainObject(intersection); + if (domainObject !== undefined) { + this.deselectAll(domainObject); + domainObject.setSelectedInteractive(true); + return; + } +} +``` + +Note the method `deselectAll`, it deselected everything except the domainObject. `setSelectedInteractive` doesn't do anything updating if it is selected from before. + +There are now two things missing. You have to selected the domain object after it is created and before it is set visible. Also deselected the others. + +The second thing you have to implement is in the view. You need to override the method `update`, so it listen to selected change. When this change appears, the children of the view is removed, the bounding box and the render target is invalidated. The last one request a redraw in Reveal. + +```typescript + public override update(change: DomainObjectChange): void { + super.update(change); + if (change.isChanged(Changes.selected)) { + this.clearMemory(); + this.invalidateRenderTarget(); + } + } +``` + +When we already do this, also add `Changes.renderStyle`, `Changes.color` and `Changes.geometry` since we need this changes later. The method `isChanged` takes multiple arguments. + +Note that we clear the memory when it change. This is a convenient method to remove all redundant data. The next time some of the data is needed, it will be generated automatically (lazy creation). The method addChildren will automatically be run when needed. + +For large objects, where `addChildren` takes more time, you cannot do this. For instance, when the color change, you only need to update the material. If the geometry change, you need to update the geometry only. If the geometry is large, you can specify which part of geometry you need to update. This is not implemented yet, since we haven't seen any use cases for this. + +> **ⓘ Try it out:** Check that the selection is working when clicking at the your objects. Also check that the object is selected when it is created. + +## Implement context dependent cursor + +Professional applications uses a context dependent cursor. To do this override `onHover`. This will have 3 states, one for picking on a `PointDomainObject`, one for picking on any other object, and one for nothing. + +```typescript + public override async onHover(event: PointerEvent): Promise { + const intersection = await this.getIntersection(event); + // Just set the cursor + if (this.getIntersectedDomainObject(intersection) !== undefined) { + this.renderTarget.setDefaultCursor(); + } else if (intersection !== undefined) { + this.renderTarget.setCrosshairCursor(); + } else { + this.renderTarget.setNavigateCursor(); + } + } +``` + +> **ⓘ Try it out:** Test that the cursor change when hovering. + +## Implement delete functionality + +To do this you override `onKey(...)`. Here is the skeleton of the method show. Fill in the rest by get the selected and remove it. + +```typescript + public override onKey(event: KeyboardEvent, down: boolean): void { + if (down && event.key === 'Delete') { + const domainObject = this.getSelected(); + // Fill in here..... + return; + } + super.onKey(event, down); + } +``` + +> **ⓘ Try it out:** Test that you can delete the points. + +## Implement user information for the selected object + +This is a attempt to generalizing the card/panel concept. A panel shows some information about the selected object. It is limited, but is a good beginning, but misses some work when it comes to translation and strings. More features will be added later. First you have to tell the framework that this domain object can generate a card (or panel). Do this by: + +```typescript + public override get hasPanelInfo(): boolean { + return true; + } +``` + +The next method generate the info for the panel. Note that the key is the translation key and there should be a translation for that key. But in the exercise we don't care, and can be anything. + +```typescript +public override getPanelInfo(): PanelInfo | undefined { + const info = new PanelInfo(); + info.setHeader('NAME', this.name); + add('X', 'X coordinate', this.center.x, Quantity.Length); + // Fill in rest here like Y, Z and length to origin for instance + return info; + + function add(key: string, fallback: string, value: number, quantity: Quantity): void { + info.add({ key, fallback, value, quantity }); + } + } +``` + +Override this method to place the panel where you like + +```typescript + public override getPanelInfoStyle(): PopupStyle { + return new PopupStyle({ bottom: 0, left: 0 }); // Here: Left-bottom corner + } +``` + +> **ⓘ Try it out:** Compile and run. Check that the panel is visualized automatically. Try the button in the panel. + +> **ⓘ Try it out:** Also, click at the `m/ft` button on the main toolbar and notice the change. + +## Example of the power of virtual methods in this framework + +Now you should override one method in your domain object: `canBeRemoved`. This returns true in the default implementation. In the your override is should return false. + +> **ⓘ Try it out:** Use the Delete key and check the panel. You shouldn't have any possibility to delete the point. The delete button in the panel should be invisible. + +## Dragging + +In order to be able to move the point around, you have to implement a dragger. This tells the framework how it should move with the mouse. You should make an object called `PointDragger` which should extend `BaseDragger`. The constructor give you some information when the dragger is starting. The method on `onPointerDrag` should do the dragging itself. + +In this example we need: + +- The domain object we like to drag +- A copy of the point at drag start +- The plane perpendicular on the mouse direction through the center at drag start +- And a offset to correction for hitting the sphere other places than in the center + +When we drag, we simple intersection the plane with the new ray and move the center to this intersection. To be accurate we take the offset correction into account. + +I give the code here, since it will take some time to figure this out. You can experiment for instance to constrain it to the horizontal plane only. Note that the input to the dragger in in CDF coordinates, not in viewer coordinates. + +```typescript +export class PointDragger extends BaseDragger { + // ================================================== + // INSTANCE FIELDS + // ================================================== + + private readonly _domainObject: PointDomainObject; + private readonly _center: Vector3; + private readonly _plane: Plane; + private readonly _offset: Vector3; // Correction for picking the sphere other places than in the center + + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor(props: CreateDraggerProps, domainObject: PointDomainObject) { + super(props); + this._domainObject = domainObject; + this._center = this._domainObject.center.clone(); + this._plane = new Plane().setFromNormalAndCoplanarPoint(this.ray.direction, this._center); + + // This is the correction for picking at the sphere other places than in the center + const planeIntersection = this.ray.intersectPlane(this._plane, new Vector3()); + if (planeIntersection === null) { + throw new Error('Failed to intersect plane'); + } + this._offset = planeIntersection.sub(this._center); + } + + // ================================================== + // OVERRIDES + // ================================================== + + public override get domainObject(): VisualDomainObject { + return this._domainObject; + } + + public override onPointerDrag(_event: PointerEvent, ray: Ray): boolean { + const planeIntersection = ray.intersectPlane(this._plane, new Vector3()); + if (planeIntersection === null) { + return false; + } + planeIntersection.sub(this._offset); + if (planeIntersection.equals(this._center)) { + return false; // No change + } + this._domainObject.center.copy(planeIntersection); + this.domainObject.notify(Changes.geometry); // Tells the domain object that the geometry has been changed + return true; + } +} +``` + +You have to tell the `PointDomainObject` to use this dragger. Then override `createDragger` from `VisualDomainObject`. The framework (here the `BaseEditTool`) will automatically create the dragger for you when the mouse start dragging. + +> **ⓘ Try it out:** Compile this code. Are you able to move the points? + +When this is done, only one thing is missing. You have to indicate in `onHover` the the point can be moved. Change from `setDefaultCursor` or `setMoveCursor`. + +> **ⓘ Try it out:** Do you see the move cursor? + +Undo dragging is missing from the architecture. It is not hard to implement within this framework and I have a pattern for this that covers most cases. It could be generally made within BaseEditTool. + +## Playing with the color and the to visual style + +If the mouse is below the selected domain object, you can try to use the mouse wheel to do some changes. +You must override the onWheel. Here is the implementation: + +```typescript + public override async onWheel(event: WheelEvent): Promise { + const intersection = await this.getIntersection(event); + const domainObject = this.getIntersectedDomainObject(intersection) as PointDomainObject; + if (domainObject === undefined || !domainObject.isSelected) { + await super.onWheel(event); + return; + } + // Change radius + const factor = 1 - Math.sign(event.deltaY) * 0.1; + domainObject.renderStyle.radius *= factor; + domainObject.notify(new DomainObjectChange(Changes.renderStyle, 'radius')); + } +``` + +And you can even to some more playing by using this code: + +```typescript +// Change color +let hsl: HSL = { h: 0, s: 0, l: 0 }; +hsl = domainObject.color.getHSL(hsl); +hsl.h = (hsl.h + Math.sign(event.deltaY) * 0.02) % 1; +domainObject.color.setHSL(hsl.h, hsl.s, hsl.l); +domainObject.notify(Changes.color); +``` + +```typescript +// Change opacity +const delta = Math.sign(event.deltaY) * 0.05; +domainObject.renderStyle.opacity = clamp(domainObject.renderStyle.opacity + delta, 0.2, 1); +domainObject.notify(new DomainObjectChange(Changes.renderStyle, 'opacity')); +``` + +You can bind the the different changes to shift and control key to make it flexible: + +```typescript +if (event.shiftKey) { + //..... copy in code here +} else if (event.ctrlKey) { + // .... copy in code here +} else { + // .... copy in code here +} +``` + +> **ⓘ Try it out:** Compile your code. Try using the scroll button to change radius, color and opacity. What happens when the mouse is not over any of the points? + +## Creating commands + +Commands is typically are user interactions outside the viewer. First you should implement a + +```typescript +export class ResetAllPointsCommand extends RenderTargetCommand { + // ================================================== + // OVERRIDES + // ================================================== + + public override get tooltip(): TranslateKey { + return { key: 'POINTS_RESET', fallback: 'Reset the visual style in all points' }; + } + + public override get icon(): string { + return 'ClearAllIcon'; + } + + protected override invokeCore(): boolean { + for (const domainObject of this.rootDomainObject.getDescendantsByType(PointDomainObject)) { + domainObject.setRenderStyle(undefined); + domainObject.notify(Changes.renderStyle); + } + return true; + } +``` + +Please implement `get isEnabled()` so the button is not enable if you don't have any `PointDomainObjects`. + +Make a similar `DeleteAllPointsCommand`. When deleting object in a list, remember to iterate in reverse order. I have done it in this way (maybe it can be done simpler?) + +```typescript + const array = Array.from(.....) + array.reverse(); + for (const domainObject of array) + // Remove the domainObject here +``` + +Also, this button must be of type `'ghost-destructive'` so you have to override `get buttonType()`. + +In order to show your commands in the user interface, you have to override the `getToolbar()` method in `PointTool`. + +```typescript +public override getToolbar(): Array { + return [ + new ResetAllPointsCommand(), + new DeleteAllPointsCommand(), + ]; +} +``` + +> **ⓘ Try it out:** Activate your tool and notice the toolbar after you have created some domain objects. Test them. + +When it is working, now you are now ready to make a `ShowAllPointCommand`. This should hide all point if they are shown and show them if the are hidden. You can use the first `PointDomainObject` you find to determine if you are visible or hidden. This should also implement the `get isChecked` method. + +Add it to the list in `getToolbar()` and test it. + +> **ⓘ Try it out:** Create some points and toggle the visible button. + +## The final touch, overriding the useDepthTest + +Here you should try another feature that is in the architecture. Override the method `useDepthTest` in the `PointView`. Let it return true only if `depthTest` in the style is true. The default implementation is return true. + +We should now investigate how the mouse picking responds on this. Keep in mind that the general intersection this is implemented in Reveal, not in this framework! + +The class above toggle the `depthTest` on all `PointDomainObject`s. In order get the current value of the `depthTest`, it simply take the first it can find. This is done in the `getDepthTest()`. Create a file and copy this code. + +```typescript +export class ShowPointsOnTopCommand extends RenderTargetCommand { + // ================================================== + // OVERRIDES + // ================================================== + + public override get tooltip(): TranslateKey { + return { key: 'POINTS_SHOW_ON_TOP', fallback: 'Show all points on top' }; + } + + public override get icon(): string { + return 'Flag'; + } + + public override get isEnabled(): boolean { + return this.getFirst() !== undefined; + } + + public override get isChecked(): boolean { + return !this.getDepthTest(); + } + + protected override invokeCore(): boolean { + const depthTest = this.getDepthTest(); + for (const domainObject of this.rootDomainObject.getDescendantsByType(PointDomainObject)) { + const style = domainObject.renderStyle; + style.depthTest = !depthTest; + domainObject.notify(Changes.renderStyle); + } + return true; + } + + // ================================================== + // INSTANCE METHODS + // ================================================== + + private getDepthTest(): boolean { + const domainObject = this.getFirst(); + if (domainObject === undefined) { + return false; + } + return domainObject.renderStyle.depthTest; + } + + private getFirst(): ExampleDomainObject | undefined { + return this.rootDomainObject.getDescendantByType(ExampleDomainObject); + } +} +``` + +Finally add `ShowPointsOnTopCommand` to the list in `getToolbar()` in the `PointTool`. + +> **ⓘ Try it out:** Create some point. Rotate the model so some points are hidden. Then check this command. Notice that the points are floating over the model. Click and double click to check the response. Uncheck and do the same. + +## Clean code? + +Lets check if your code is clean when it comes to dependencies. Ask yourself: + +1. How many references do I have to the `PointDragger`. +2. How many references do I have to the `PointView`. +3. How many references do I have to the `PointRenderStyle`. +4. How many references do I have from files in the directory I work in to the rest of the system. Please count them. + +# More advanced exercises + +## Using a folder + +Now all the `PointDomainObject` are located just below the root. You can make a domain object called `PointFolder`, it can be named `PointFolderDomainObject`, but as a convention, folders is a sort of domain that contains others. + +`PointFolder` will inherit directly from `DomainObject` since it don't have a view. + +You can then move the `createRenderStyle` to the `PointFolder`, and override `renderStyleRoot` in `PointDomainObject` to get so it return its parent, and finally override `isRenderStyleRoot` in the `PointFolder`. Then you will have only one `RenderStyle` for all `PointDomainObject`. + +You must also change the: + +```typescript +this.rootDomainObject.addChildInteractive(domainObject); +``` + +to: + +```typescript +let folder = this.rootDomainObject.getDescendantByType(PointFolder); +if (folder === undefined) { + // Create the folder if it doesn't exist + folder = new PointFolder(); + this.rootDomainObject.addChildInteractive(folder); +} +folder.addChildInteractive(domainObject); +``` + +The rest of the code should work without any change. + +Now you can use `PointFolder.setVisibleInteractive`, to toggle visibility at all `PointDomainObject`s. + +## One domain object for all the points + +This is more challenging, but should be done if number of points typically is large. + +- You must implement your selection mechanism yourself in the domain object by for instance keep the index of the selected point or a list of multi selection is allowed. +- You have to do changes in the tool. +- The view should show all points, in addition the selected point different then the other. +- In the view the `intersectIfCloser` should be override so the index of your point should + be return with the `CustomObjectIntersection`. You can use the field userData to have the index + of the closest intersected point. +- None of this is particularly hard in this architecture. + +## Focus on hover + +Focus is when you mark the object behind the mouse when hover. You can easily create a focus mechanism. See how this is done the the `MeasureLineDomainObject` (simple) or `MeasureBoxDomainObject` (more advance hover where hover is dependent on where on the box the mouse hover on). + +## Multi selection + +Normally in other application you can expand or turn off selection with the control key. This should also work well in this framework. You will need to do adjustments in the `PointTool` code. The panel is implemented so it shows the last selected regardless of how many object you have selected. diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts index d5881c34011..f4857350952 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/DeleteAllExamplesCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../../base/commands/RenderTargetCommand'; @@ -20,6 +19,10 @@ export class DeleteAllExamplesCommand extends RenderTargetCommand { return 'Delete'; } + public override get buttonType(): string { + return 'ghost-destructive'; + } + public override get isEnabled(): boolean { const first = this.getFirst(); return first !== undefined && first.canBeRemoved; diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts index 9ce568a2209..7e38aff7db5 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../../base/commands/RenderTargetCommand'; @@ -14,11 +13,11 @@ export class ResetAllExamplesCommand extends RenderTargetCommand { // ================================================== public override get tooltip(): TranslateKey { - return { key: 'EXAMPLES_RESET', fallback: 'Reset all examples' }; + return { key: 'EXAMPLES_RESET', fallback: 'the visual style for all examples' }; } public override get icon(): string { - return 'Copy'; + return 'ClearAllIcon'; } public override get isEnabled(): boolean { diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts index 36568a02eb2..312f41821ac 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowAllExamplesCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../../base/commands/RenderTargetCommand'; diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts index 7ed28fdc064..1e67d9a9463 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ShowExamplesOnTopCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../../base/commands/RenderTargetCommand'; @@ -18,7 +17,7 @@ export class ShowExamplesOnTopCommand extends RenderTargetCommand { } public override get icon(): string { - return 'EyeShow'; + return 'Flag'; } public override get isEnabled(): boolean { diff --git a/react-components/src/architecture/concrete/terrainDomainObject/SetTerrainVisibleCommand.ts b/react-components/src/architecture/concrete/terrainDomainObject/SetTerrainVisibleCommand.ts index 08869ee34e0..f4063797870 100644 --- a/react-components/src/architecture/concrete/terrainDomainObject/SetTerrainVisibleCommand.ts +++ b/react-components/src/architecture/concrete/terrainDomainObject/SetTerrainVisibleCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; diff --git a/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts b/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts index 7d824428dd2..04d0cf777c8 100644 --- a/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts +++ b/react-components/src/architecture/concrete/terrainDomainObject/TerrainThreeView.ts @@ -198,7 +198,7 @@ function updateSolidMaterial( is2D: boolean = false ): void { material.side = DoubleSide; // the terrain must be seen from both side - material.polygonOffset = style.showContours; // Because of the countours to be visible + material.polygonOffset = style.showContours; // Because of the contours to be visible material.polygonOffsetFactor = 1; material.polygonOffsetUnits = 4.0; diff --git a/react-components/src/architecture/concrete/terrainDomainObject/UpdateTerrainCommand.ts b/react-components/src/architecture/concrete/terrainDomainObject/UpdateTerrainCommand.ts index fe8ef24a2ef..a08401a44fb 100644 --- a/react-components/src/architecture/concrete/terrainDomainObject/UpdateTerrainCommand.ts +++ b/react-components/src/architecture/concrete/terrainDomainObject/UpdateTerrainCommand.ts @@ -1,6 +1,5 @@ /*! * Copyright 2024 Cognite AS - * BaseTool: Base class for the tool are used to interact with the render target. */ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; diff --git a/react-components/src/components/Architecture/CommandButton.tsx b/react-components/src/components/Architecture/CommandButton.tsx index 921af49e30b..3e884ff5df2 100644 --- a/react-components/src/components/Architecture/CommandButton.tsx +++ b/react-components/src/components/Architecture/CommandButton.tsx @@ -67,10 +67,15 @@ export const CommandButton = ({ } const placement = isHorizontal ? 'top' : 'right'; const { key, fallback } = newCommand.tooltip; + // This was the only way it went through compiler: (more bytton types will be added in the future) + const type = newCommand.buttonType; + if (type !== 'ghost' && type !== 'ghost-destructive') { + return <>; + } return ( - - ); -}; - -const exampleHighQualitySettings: QualitySettings = { - cadBudget: { - maximumRenderCost: 95000000, - highDetailProximityThreshold: 100 - }, - pointCloudBudget: { - numberOfPoints: 12000000 - }, - resolutionOptions: { - maxRenderResolution: Infinity, - movingCameraResolutionFactor: 1 - } -}; - -const exampleLowQualitySettings: QualitySettings = { - cadBudget: { - maximumRenderCost: 10_000_000, - highDetailProximityThreshold: 100 - }, - pointCloudBudget: { - numberOfPoints: 2_000_000 - }, - resolutionOptions: { - maxRenderResolution: 1e5, - movingCameraResolutionFactor: 1 - } -}; - export const Main: Story = { args: { addModelOptions: getAddModelOptionsFromUrl('/primitives') @@ -83,13 +29,7 @@ export const Main: Story = { render: ({ addModelOptions }) => { return ( - - @@ -111,17 +51,3 @@ function StoryContent({ addModelOptions }: { addModelOptions: AddModelOptions }) ); } - -function FitToUrlCameraState(): ReactElement { - const getCameraState = useGetCameraStateFromUrlParam(); - const cameraNavigation = useCameraNavigation(); - - useEffect(() => { - signalStoryReadyForScreenshot(); - const currentCameraState = getCameraState(); - if (currentCameraState === undefined) return; - cameraNavigation.fitCameraToState(currentCameraState); - }, []); - - return <>; -} From f8b2cc842a1b8bf0ea9ffe80fe0bdb0db4f21b44 Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Sun, 9 Jun 2024 21:12:51 +0200 Subject: [PATCH 4/6] Smaller change --- .../base/domainObjectsHelpers/DomainObjectChange.ts | 1 - .../commands/ResetAllExamplesCommand.ts | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts b/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts index 31f25b49ff9..9969c22329e 100644 --- a/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts +++ b/react-components/src/architecture/base/domainObjectsHelpers/DomainObjectChange.ts @@ -110,7 +110,6 @@ export class DomainObjectChange { // ================================================== // LOCAL HELPER CLASS // ================================================== - class ChangedDescription { public change: symbol; public fieldName: string | undefined; diff --git a/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts b/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts index 7e38aff7db5..99677cb9c05 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts +++ b/react-components/src/architecture/concrete/exampleDomainObject/commands/ResetAllExamplesCommand.ts @@ -13,11 +13,14 @@ export class ResetAllExamplesCommand extends RenderTargetCommand { // ================================================== public override get tooltip(): TranslateKey { - return { key: 'EXAMPLES_RESET', fallback: 'the visual style for all examples' }; + return { + key: 'EXAMPLES_RESET', + fallback: 'Reset the visual style for all examples to default' + }; } public override get icon(): string { - return 'ClearAllIcon'; + return 'ClearAll'; } public override get isEnabled(): boolean { From 0e3f771494ffd51530ea8f6e661ce0562df2b89b Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Mon, 10 Jun 2024 06:44:34 +0200 Subject: [PATCH 5/6] Fixes --- .../ToggleMetricUnitsCommand.ts | 8 ++-- .../concrete/exampleDomainObject/README.md | 39 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/react-components/src/architecture/base/concreteCommands/ToggleMetricUnitsCommand.ts b/react-components/src/architecture/base/concreteCommands/ToggleMetricUnitsCommand.ts index a3862f5e045..ccac8ad91db 100644 --- a/react-components/src/architecture/base/concreteCommands/ToggleMetricUnitsCommand.ts +++ b/react-components/src/architecture/base/concreteCommands/ToggleMetricUnitsCommand.ts @@ -20,15 +20,13 @@ export class ToggleMetricUnitsCommand extends RenderTargetCommand { } public override get isChecked(): boolean { - const { renderTarget } = this; - return renderTarget.rootDomainObject.unitSystem.isMetric; + return this.rootDomainObject.unitSystem.isMetric; } protected override invokeCore(): boolean { - const { renderTarget } = this; - const unitSystem = renderTarget.rootDomainObject.unitSystem; + const unitSystem = this.rootDomainObject.unitSystem; unitSystem.isMetric = !unitSystem.isMetric; - renderTarget.rootDomainObject.notifyDescendants(Changes.unit); + this.rootDomainObject.notifyDescendants(Changes.unit); return true; } } diff --git a/react-components/src/architecture/concrete/exampleDomainObject/README.md b/react-components/src/architecture/concrete/exampleDomainObject/README.md index 42c1f3ad28c..4bf88809b68 100644 --- a/react-components/src/architecture/concrete/exampleDomainObject/README.md +++ b/react-components/src/architecture/concrete/exampleDomainObject/README.md @@ -413,11 +413,11 @@ You can bind the the different changes to shift and control key to make it flexi ```typescript if (event.shiftKey) { - //..... copy in code here + //..... paste code here } else if (event.ctrlKey) { - // .... copy in code here + // .... paste code here } else { - // .... copy in code here + // .... paste code here } ``` @@ -425,7 +425,19 @@ if (event.shiftKey) { ## Creating commands -Commands is typically are user interactions outside the viewer. First you should implement a +Commands is typically are user interactions outside the viewer. First you should implement a command that reset the render style for all your points. + +When working on commands, it is often need for traversing the rootDomainObject. In the domain object you will find some families of convenience methods for this. These are: + +- `getChild*(...)` Methods for getting a specific child +- `get[ThisAnd]Descendants*(...)` Methods for iterating on a specific descendants +- `getDescendantBy*(...)` Methods for getting a specific descendant +- `get[ThisAnd]Ancestors*(...)` Methods for iterating on a specific ancestors +- `get[ThisAnd]AncestorBy*(...)` Methods for getting a specific ancestor + +By using these function, you can get access to all domain objects you need. + +Here is the code you can start with: ```typescript export class ResetAllPointsCommand extends RenderTargetCommand { @@ -452,7 +464,7 @@ export class ResetAllPointsCommand extends RenderTargetCommand { Please implement `get isEnabled()` so the button is not enable if you don't have any `PointDomainObjects`. -Make a similar `DeleteAllPointsCommand`. When deleting object in a list, remember to iterate in reverse order. I have done it in this way (maybe it can be done simpler?) +Make a similar `DeleteAllPointsCommand`. When deleting object by a collection, remember to iterate in reverse order. I have done it in this way (maybe it can be done simpler?) ```typescript const array = Array.from(.....) @@ -461,7 +473,7 @@ Make a similar `DeleteAllPointsCommand`. When deleting object in a list, remembe // Remove the domainObject here ``` -Also, this button must be of type `'ghost-destructive'` so you have to override `get buttonType()`. +Also, this button must be of type `'ghost-destructive'` so you have to override `get buttonType()`. Use the `'Delete'` icon. In order to show your commands in the user interface, you have to override the `getToolbar()` method in `PointTool`. @@ -476,19 +488,19 @@ public override getToolbar(): Array { > **ⓘ Try it out:** Activate your tool and notice the toolbar after you have created some domain objects. Test them. -When it is working, now you are now ready to make a `ShowAllPointCommand`. This should hide all point if they are shown and show them if the are hidden. You can use the first `PointDomainObject` you find to determine if you are visible or hidden. This should also implement the `get isChecked` method. +When it is working, now you are now ready to make a `ShowAllPointCommand`. This should hide all point if they are shown and show them if the are hidden. You can simply take the first `PointDomainObject` you find to determine if they are visible or hidden. This should also implement the `get isChecked` method. This can be done simpler, see section **Using a folder** below. Add it to the list in `getToolbar()` and test it. > **ⓘ Try it out:** Create some points and toggle the visible button. -## The final touch, overriding the useDepthTest +## The final touch - manipulate the DepthTest -Here you should try another feature that is in the architecture. Override the method `useDepthTest` in the `PointView`. Let it return true only if `depthTest` in the style is true. The default implementation is return true. +Here you should try another feature that is in the architecture. Override the method `useDepthTest` in the `PointView`. Let it return true only if `depthTest` in the style is true. The default implementation returns true. -We should now investigate how the mouse picking responds on this. Keep in mind that the general intersection this is implemented in Reveal, not in this framework! +We should now investigate how the mouse picking responds on this. Keep in mind that the general intersection is implemented in Reveal, not in this framework! -The class above toggle the `depthTest` on all `PointDomainObject`s. In order get the current value of the `depthTest`, it simply take the first it can find. This is done in the `getDepthTest()`. Create a file and copy this code. +The class above toggle the `depthTest` on all `PointDomainObject`s. In order get the current value of the `depthTest`, simply take the first it can find. This is done in the `getDepthTest()`. Create a file and copy this code. ```typescript export class ShowPointsOnTopCommand extends RenderTargetCommand { @@ -550,8 +562,9 @@ Lets check if your code is clean when it comes to dependencies. Ask yourself: 1. How many references do I have to the `PointDragger`. 2. How many references do I have to the `PointView`. -3. How many references do I have to the `PointRenderStyle`. -4. How many references do I have from files in the directory I work in to the rest of the system. Please count them. +3. How many references do I have from files in the directory I work in to the rest of the system. Please count them. + +> ⓘ If you have done all exercises until now you are finished. You will receive a diploma for your effort and attention. You are now able to use this architecture and hopefully get some good ideas to use similar architecture elsewhere. # More advanced exercises From c5504de79c66704d956d635c1c4e18f37dff83ba Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Mon, 10 Jun 2024 07:29:51 +0200 Subject: [PATCH 6/6] More typos --- .../base/commands/BaseEditTool.ts | 27 +++++++++---------- .../base/renderTarget/CommandsController.ts | 2 +- .../base/renderTarget/RevealRenderTarget.ts | 2 +- .../base/utilities/colors/colorMaps.ts | 2 +- .../utilities/extensions/arrayExtensions.ts | 2 +- .../utilities/extensions/mathExtensions.ts | 2 +- .../base/utilities/geometry/Polyline.ts | 2 +- .../base/utilities/geometry/Range1.ts | 8 +++--- .../concrete/axis/AxisRenderStyle.ts | 2 +- .../concrete/axis/AxisThreeView.ts | 6 ++--- .../geometry/RegularGrid2.ts | 2 +- .../geometry/createFractalRegularGrid2.ts | 16 +++++------ 12 files changed, 36 insertions(+), 37 deletions(-) diff --git a/react-components/src/architecture/base/commands/BaseEditTool.ts b/react-components/src/architecture/base/commands/BaseEditTool.ts index 34ea748fc63..06ca7894562 100644 --- a/react-components/src/architecture/base/commands/BaseEditTool.ts +++ b/react-components/src/architecture/base/commands/BaseEditTool.ts @@ -3,7 +3,6 @@ */ import { NavigationTool } from './NavigationTool'; -import { type DomainObject } from '../domainObjects/DomainObject'; import { isDomainObjectIntersection } from '../domainObjectsHelpers/DomainObjectIntersection'; import { type BaseDragger } from '../domainObjectsHelpers/BaseDragger'; import { VisualDomainObject } from '../domainObjects/VisualDomainObject'; @@ -80,14 +79,13 @@ export abstract class BaseEditTool extends NavigationTool { * @param domainObject - The domain object to be accepted. * @returns `true` if the domain object can be accepted, `false` otherwise. */ - protected canBeSelected(_domainObject: DomainObject): boolean { + protected canBeSelected(_domainObject: VisualDomainObject): boolean { return false; } /** * Override this function to create custom dragger - * with other creation logic. Otherwise createDragger in - * the DomainObject itself + * with other creation logic. */ protected async createDragger(event: PointerEvent): Promise { const intersection = await this.getIntersection(event); @@ -120,7 +118,7 @@ export abstract class BaseEditTool extends NavigationTool { */ protected deselectAll(except?: VisualDomainObject | undefined): void { const { rootDomainObject } = this; - for (const domainObject of rootDomainObject.getDescendants()) { + for (const domainObject of rootDomainObject.getDescendantsByType(VisualDomainObject)) { if (!this.canBeSelected(domainObject)) { continue; } @@ -136,9 +134,9 @@ export abstract class BaseEditTool extends NavigationTool { * Use only if multi selection is expected. * @returns A generator that yields each selected domain object. */ - protected *getAllSelected(): Generator { + protected *getAllSelected(): Generator { const { rootDomainObject } = this; - for (const domainObject of rootDomainObject.getDescendants()) { + for (const domainObject of rootDomainObject.getDescendantsByType(VisualDomainObject)) { if (!domainObject.isSelected) { continue; } @@ -150,13 +148,13 @@ export abstract class BaseEditTool extends NavigationTool { } /** - * Retrieves the selected DomainObject. + * Retrieves the selected VisualDomainObject. * Use only if single selection is expected. * @returns The selected DomainObject, or undefined if no object is selected. */ - protected getSelected(): DomainObject | undefined { + protected getSelected(): VisualDomainObject | undefined { const { rootDomainObject } = this; - for (const domainObject of rootDomainObject.getDescendants()) { + for (const domainObject of rootDomainObject.getDescendantsByType(VisualDomainObject)) { if (!domainObject.isSelected) { continue; } @@ -179,12 +177,13 @@ export abstract class BaseEditTool extends NavigationTool { if (!isDomainObjectIntersection(intersection)) { return undefined; } - if (!this.canBeSelected(intersection.domainObject)) { + const { domainObject } = intersection; + if (!(domainObject instanceof VisualDomainObject)) { return undefined; - } else if (intersection.domainObject instanceof VisualDomainObject) { - return intersection.domainObject; - } else { + } + if (!this.canBeSelected(domainObject)) { return undefined; } + return domainObject; } } diff --git a/react-components/src/architecture/base/renderTarget/CommandsController.ts b/react-components/src/architecture/base/renderTarget/CommandsController.ts index 20420a5cf2b..acf184767d7 100644 --- a/react-components/src/architecture/base/renderTarget/CommandsController.ts +++ b/react-components/src/architecture/base/renderTarget/CommandsController.ts @@ -20,7 +20,7 @@ export class CommandsController extends PointerEvents { private readonly _pointerEventsTarget: PointerEventsTarget; // ================================================== - // CONTRUCTORS + // CONSTRUCTOR // ================================================== constructor(domElement: HTMLElement) { diff --git a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts index 1210970cc66..f1ad5caf8fe 100644 --- a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts +++ b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts @@ -45,7 +45,7 @@ export class RevealRenderTarget { private _config: BaseRevealConfig | undefined = undefined; // ================================================== - // CONTRUCTORS + // CONSTRUCTOR // ================================================== constructor(viewer: Cognite3DViewer) { diff --git a/react-components/src/architecture/base/utilities/colors/colorMaps.ts b/react-components/src/architecture/base/utilities/colors/colorMaps.ts index b24f4666102..091ec94e395 100644 --- a/react-components/src/architecture/base/utilities/colors/colorMaps.ts +++ b/react-components/src/architecture/base/utilities/colors/colorMaps.ts @@ -8,7 +8,7 @@ import { Color } from 'three'; import { ColorMapType } from './ColorMapType'; import { getColorFromBytes as getFromBytes } from './colorExtensions'; -let colorMaps: Map | undefined; // Act as a sigleton +let colorMaps: Map | undefined; // Act as a singleton // ================================================== // PUBLIC FUNCTIONS: diff --git a/react-components/src/architecture/base/utilities/extensions/arrayExtensions.ts b/react-components/src/architecture/base/utilities/extensions/arrayExtensions.ts index d56bbbff541..11f776c209f 100644 --- a/react-components/src/architecture/base/utilities/extensions/arrayExtensions.ts +++ b/react-components/src/architecture/base/utilities/extensions/arrayExtensions.ts @@ -3,7 +3,7 @@ */ /* - * Utilitys function for missing array methods + * Utility function for missing array methods * Use there function in order top increase the readability of your code */ diff --git a/react-components/src/architecture/base/utilities/extensions/mathExtensions.ts b/react-components/src/architecture/base/utilities/extensions/mathExtensions.ts index 70625ce2e96..92be9b308cb 100644 --- a/react-components/src/architecture/base/utilities/extensions/mathExtensions.ts +++ b/react-components/src/architecture/base/utilities/extensions/mathExtensions.ts @@ -65,7 +65,7 @@ export function square(value: number): number { /** * Round a number closest to one of these values: 2*10^N, 2.5*10^N, 5*10^N or 10*10^N. - * This is used to give axies a natural increment between the ticks or + * This is used to give axes a natural increment between the ticks or * contour intervals on a terrain surface * @param increment - The value to be rounded * @returns The rounded value diff --git a/react-components/src/architecture/base/utilities/geometry/Polyline.ts b/react-components/src/architecture/base/utilities/geometry/Polyline.ts index 0b6d8a1c400..6fc712a9a97 100644 --- a/react-components/src/architecture/base/utilities/geometry/Polyline.ts +++ b/react-components/src/architecture/base/utilities/geometry/Polyline.ts @@ -60,7 +60,7 @@ export class Polyline extends Points { for (let index = 1; index <= n; index++) { p1.copy(this.list[index % n]); - p1.sub(first); // Translate down to first point, to increase acceracy + p1.sub(first); // Translate down to first point, to increase accuracy area += getHorizontalCrossProduct(p0, p1); p0.copy(p1); } diff --git a/react-components/src/architecture/base/utilities/geometry/Range1.ts b/react-components/src/architecture/base/utilities/geometry/Range1.ts index 4144fb8d33c..c27d93b9d80 100644 --- a/react-components/src/architecture/base/utilities/geometry/Range1.ts +++ b/react-components/src/architecture/base/utilities/geometry/Range1.ts @@ -149,7 +149,7 @@ export class Range1 { } public *getFastTicks(increment: number, tolerance: number): Generator { - // This method overwrites this (optimalization) + // This method overwrites this (optimization) if (!this.roundByInc(-increment)) { return; } @@ -163,14 +163,14 @@ export class Range1 { } public getBoldIncrement(increment: number, every = 2): number { - let numerOfTicks = 0; + let numberOfTicks = 0; const boldIncrement = increment * every; for (const tick of this.getTicks(increment)) { if (!isIncrement(tick, boldIncrement)) { continue; } - numerOfTicks += 1; - if (numerOfTicks > 2) { + numberOfTicks += 1; + if (numberOfTicks > 2) { return boldIncrement; } } diff --git a/react-components/src/architecture/concrete/axis/AxisRenderStyle.ts b/react-components/src/architecture/concrete/axis/AxisRenderStyle.ts index ba594132ba0..b67f91f4765 100644 --- a/react-components/src/architecture/concrete/axis/AxisRenderStyle.ts +++ b/react-components/src/architecture/concrete/axis/AxisRenderStyle.ts @@ -25,7 +25,7 @@ export class AxisRenderStyle extends RenderStyle { public showAxisTicks = true; public showGrid = true; - public numberOfTicks = 40; // Appoximately number of ticks for the largest axis + public numberOfTicks = 40; // Approximately number of ticks for the largest axis public tickLength = 0.005; // In fraction of the bounding box diagonal public tickFontSize = 2; // In fraction of the real tickLength public axisLabelFontSize = 4; // In fraction of the real tickLength diff --git a/react-components/src/architecture/concrete/axis/AxisThreeView.ts b/react-components/src/architecture/concrete/axis/AxisThreeView.ts index 9f4c1cba658..5cf8ebd5c0a 100644 --- a/react-components/src/architecture/concrete/axis/AxisThreeView.ts +++ b/react-components/src/architecture/concrete/axis/AxisThreeView.ts @@ -195,7 +195,7 @@ export class AxisThreeView extends GroupThreeView { if (style.showAxis) { this.addAxisLine(style, i0, i1, dimension, faceIndex1, faceIndex2); } - this.addAxisTickmarksAndLabels(props, i0, i1, dimension, faceIndex1, faceIndex2); + this.addAxisTickMarksAndLabels(props, i0, i1, dimension, faceIndex1, faceIndex2); } private addAxisLine( @@ -223,7 +223,7 @@ export class AxisThreeView extends GroupThreeView { } } - private addAxisTickmarksAndLabels( + private addAxisTickMarksAndLabels( props: AxisProps, i0: number, i1: number, @@ -606,7 +606,7 @@ function incrementToString(value: number): string { // To get better rounded values, I wrote this myself: Multiply by some high integer and round it, then // convert to text, and insert the comma manually afterwards. - // Small number get less accurate result in tjhis algorithm,, so use the default string conversion. + // Small number get less accurate result in this algorithm,, so use the default string conversion. if (Math.abs(value) < 0.001) { return `${value}`; } diff --git a/react-components/src/architecture/concrete/terrainDomainObject/geometry/RegularGrid2.ts b/react-components/src/architecture/concrete/terrainDomainObject/geometry/RegularGrid2.ts index 513b280c5bd..ad28fd60529 100644 --- a/react-components/src/architecture/concrete/terrainDomainObject/geometry/RegularGrid2.ts +++ b/react-components/src/architecture/concrete/terrainDomainObject/geometry/RegularGrid2.ts @@ -336,7 +336,7 @@ export class RegularGrid2 extends Grid2 { let count = 0; let sum = 0; - // New value = (Sum the surrunding values + 2 * Current value) / N + // New value = (Sum the surrounding values + 2 * Current value) / N for (let ii = iMin; ii <= iMax; ii++) for (let jj = jMin; jj <= jMax; jj++) { if (ii === i && jj === j) continue; diff --git a/react-components/src/architecture/concrete/terrainDomainObject/geometry/createFractalRegularGrid2.ts b/react-components/src/architecture/concrete/terrainDomainObject/geometry/createFractalRegularGrid2.ts index 4de57908fa7..e0c3edf6332 100644 --- a/react-components/src/architecture/concrete/terrainDomainObject/geometry/createFractalRegularGrid2.ts +++ b/react-components/src/architecture/concrete/terrainDomainObject/geometry/createFractalRegularGrid2.ts @@ -10,7 +10,7 @@ import { RegularGrid2 } from './RegularGrid2'; export function createFractalRegularGrid2( boundingBox: Range3, powerOf2: number = 8, - dampning: number = 0.7, + damping: number = 0.7, smoothNumberOfPasses: number = 2, rotationAngle: number = 0 ): RegularGrid2 { @@ -30,7 +30,7 @@ export function createFractalRegularGrid2( grid.setZ(i0, j1, getRandomGaussian(0, stdDev)); grid.setZ(i1, j1, getRandomGaussian(0, stdDev)); - subDivide(grid, i0, j0, i1, j1, stdDev, powerOf2, dampning); + subDivide(grid, i0, j0, i1, j1, stdDev, powerOf2, damping); grid.origin.x = boundingBox.x.min; grid.origin.y = boundingBox.y.min; @@ -78,7 +78,7 @@ function subDivide( j2: number, stdDev: number, level: number, - dampning: number + damping: number ): void { if (i2 - i0 <= 1 && j2 - j0 <= 1) { return; // Nothing more to update @@ -86,7 +86,7 @@ function subDivide( if (i2 - i0 !== j2 - j0) { throw Error('Logical bug, the grid should be a square'); } - stdDev *= dampning; + stdDev *= damping; let z = 0; z += setValueBetween(grid, i0, j0, i2, j0, stdDev); z += setValueBetween(grid, i0, j2, i2, j2, stdDev); @@ -102,8 +102,8 @@ function subDivide( const i1 = Math.trunc((i0 + i2) / 2); const j1 = Math.trunc((j0 + j2) / 2); - subDivide(grid, i0, j0, i1, j1, stdDev, level, dampning); - subDivide(grid, i0, j1, i1, j2, stdDev, level, dampning); - subDivide(grid, i1, j0, i2, j1, stdDev, level, dampning); - subDivide(grid, i1, j1, i2, j2, stdDev, level, dampning); + subDivide(grid, i0, j0, i1, j1, stdDev, level, damping); + subDivide(grid, i0, j1, i1, j2, stdDev, level, damping); + subDivide(grid, i1, j0, i2, j1, stdDev, level, damping); + subDivide(grid, i1, j1, i2, j2, stdDev, level, damping); }