From 0dcbb9feec7971a2a29c4105ecb0e8384d322797 Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Tue, 11 Jun 2024 17:21:25 +0200 Subject: [PATCH 01/37] First commit --- .../concrete/box/BoxDomainObject.ts | 314 ++++++++++++++++++ .../concrete/box/LineDomainObject.ts | 205 ++++++++++++ .../boxDomainObject/MeasureBoxCreator.ts | 2 +- .../boxDomainObject/MeasureBoxDomainObject.ts | 38 ++- .../boxDomainObject/MeasureBoxRenderStyle.ts | 4 +- .../boxDomainObject/MeasureBoxView.ts | 7 +- .../boxDomainObject/MeasureDomainObject.ts | 54 --- .../MeasureLineDomainObject.ts | 34 +- .../boxDomainObject/MeasureLineRenderStyle.ts | 4 +- .../boxDomainObject/MeasureLineView.ts | 7 +- .../boxDomainObject/MeasurementTool.ts | 43 ++- .../ShowMeasurementsOnTopCommand.ts | 29 +- ...asureRenderStyle.ts => TextRenderStyle.ts} | 2 +- 13 files changed, 644 insertions(+), 99 deletions(-) create mode 100644 react-components/src/architecture/concrete/box/BoxDomainObject.ts create mode 100644 react-components/src/architecture/concrete/box/LineDomainObject.ts delete mode 100644 react-components/src/architecture/concrete/boxDomainObject/MeasureDomainObject.ts rename react-components/src/architecture/concrete/boxDomainObject/{MeasureRenderStyle.ts => TextRenderStyle.ts} (92%) diff --git a/react-components/src/architecture/concrete/box/BoxDomainObject.ts b/react-components/src/architecture/concrete/box/BoxDomainObject.ts new file mode 100644 index 00000000000..4f1bc5cd8c4 --- /dev/null +++ b/react-components/src/architecture/concrete/box/BoxDomainObject.ts @@ -0,0 +1,314 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { MeasureBoxRenderStyle } from './MeasureBoxRenderStyle'; +import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; +import { type ThreeView } from '../../base/views/ThreeView'; +import { MeasureBoxView } from './MeasureBoxView'; +import { Box3, Color, Matrix4, Vector3 } from 'three'; +import { Changes } from '../../base/domainObjectsHelpers/Changes'; +import { BoxFace } from '../../base/utilities/box/BoxFace'; +import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; +import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; +import { type BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; +import { type BaseDragger } from '../../base/domainObjectsHelpers/BaseDragger'; +import { MeasureBoxDragger } from './MeasureBoxDragger'; +import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; +import { radToDeg } from 'three/src/math/MathUtils.js'; +import { + VisualDomainObject, + type CreateDraggerProps +} from '../../base/domainObjects/VisualDomainObject'; +import { Range3 } from '../../base/utilities/geometry/Range3'; +import { CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal'; +import { type DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; +import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; +import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; + +export const MIN_BOX_SIZE = 0.01; + +export class BoxDomainObject extends VisualDomainObject { + // ================================================== + // INSTANCE FIELDS + // ================================================== + + public readonly size = new Vector3().setScalar(MIN_BOX_SIZE); + public readonly center = new Vector3(); + public zRotation = 0; // Angle in radians in interval [0, 2*Pi> + + // For focus when edit in 3D (Used when isSelected is true only) + public focusType: FocusType = FocusType.None; + public focusFace: BoxFace | undefined = undefined; + private readonly _measureType: MeasureType; + + // ================================================== + // INSTANCE PROPERTIES + // ================================================== + + public get renderStyle(): MeasureBoxRenderStyle { + return this.getRenderStyle() as MeasureBoxRenderStyle; + } + + public get measureType(): MeasureType { + return this._measureType; + } + + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor(measureType: MeasureType) { + super(); + this._measureType = measureType; + this.color = new Color(Color.NAMES.magenta); + } + + // ================================================== + // OVERRIDES of DomainObject + // ================================================== + + public override get icon(): string { + return getIconByMeasureType(this.measureType); + } + + public override get typeName(): string { + return getNameByMeasureType(this.measureType); + } + + public override createRenderStyle(): RenderStyle | undefined { + return new MeasureBoxRenderStyle(); + } + + public override createDragger(props: CreateDraggerProps): BaseDragger | undefined { + const pickInfo = props.intersection.userData as BoxPickInfo; + if (pickInfo === undefined) { + return undefined; // If the BoxPickInfo isn't specified, no dragger is created + } + return new MeasureBoxDragger(props, this); + } + + protected override notifyCore(change: DomainObjectChange): void { + super.notifyCore(change); + + // Experimental code for crop box (let it stay here) + // if (this.isUseAsCropBox) { + // if (change.isChanged(Changes.deleted)) { + // this.setUseAsCropBox(false); + // } + // if (change.isChanged(Changes.geometry)) { + // this.setUseAsCropBox(true); + // } + // } else if (change.isChanged(Changes.selected) && this.isSelected) { + // const root = this.root as RootDomainObject; + // if (root instanceof RootDomainObject && root.renderTarget.isGlobalCropBoxActive) { + // this.setUseAsCropBox(true); + // } + // } + } + + public override get hasPanelInfo(): boolean { + return true; + } + + public override getPanelInfoStyle(): PopupStyle { + // bottom = 66 because the toolbar is below + return new PopupStyle({ bottom: 66, left: 0 }); + } + + public override getPanelInfo(): PanelInfo | undefined { + const info = new PanelInfo(); + const { measureType } = this; + const isFinished = this.focusType !== FocusType.Pending; + + switch (measureType) { + case MeasureType.HorizontalArea: + info.setHeader('MEASUREMENTS_HORIZONTAL_AREA', 'Horizontal area'); + break; + case MeasureType.VerticalArea: + info.setHeader('MEASUREMENTS_VERTICAL_AREA', 'Vertical area'); + break; + case MeasureType.Volume: + info.setHeader('MEASUREMENTS_VOLUME', 'Volume'); + break; + } + if (isFinished || isValidSize(this.size.x)) { + add('MEASUREMENTS_LENGTH', 'Length', this.size.x, Quantity.Length); + } + if (measureType !== MeasureType.VerticalArea && (isFinished || isValidSize(this.size.y))) { + add('MEASUREMENTS_DEPTH', 'Depth', this.size.y, Quantity.Length); + } + if (measureType !== MeasureType.HorizontalArea && (isFinished || isValidSize(this.size.z))) { + add('MEASUREMENTS_HEIGHT', 'Height', this.size.z, Quantity.Length); + } + if (measureType !== MeasureType.Volume && (isFinished || this.hasArea)) { + add('MEASUREMENTS_AREA', 'Area', this.area, Quantity.Area); + } + if (measureType === MeasureType.Volume && (isFinished || this.hasHorizontalArea)) { + add('MEASUREMENTS_HORIZONTAL_AREA', 'Horizontal area', this.horizontalArea, Quantity.Area); + } + if (measureType === MeasureType.Volume && (isFinished || this.hasVolume)) { + add('MEASUREMENTS_VOLUME', 'Volume', this.volume, Quantity.Volume); + } + // I forgot to add text for rotation angle before the deadline, so I used a icon instead. + if (this.zRotation !== 0 && isFinished) { + info.add({ + key: '', + icon: 'Angle', + value: radToDeg(this.zRotation), + quantity: Quantity.Degrees + }); + } + return info; + + function add(key: string, fallback: string, value: number, quantity: Quantity): void { + info.add({ key, fallback, value, quantity }); + } + } + + // ================================================== + // OVERRIDES of VisualDomainObject + // ================================================== + + protected override createThreeView(): ThreeView | undefined { + return new MeasureBoxView(); + } + + // ================================================== + // INSTANCE METHODS: Getters/Properties + // ================================================== + + public get diagonal(): number { + return this.size.length(); + } + + public get hasArea(): boolean { + let count = 0; + if (isValidSize(this.size.x)) count++; + if (isValidSize(this.size.y)) count++; + if (isValidSize(this.size.z)) count++; + return count >= 2; + } + + public get area(): number { + switch (this.measureType) { + case MeasureType.HorizontalArea: + return this.size.x * this.size.y; + case MeasureType.VerticalArea: + return this.size.x * this.size.z; + case MeasureType.Volume: { + const a = this.size.x * this.size.y + this.size.y * this.size.z + this.size.z * this.size.x; + return a * 2; + } + default: + throw new Error('Unknown MeasureType type'); + } + } + + public get hasHorizontalArea(): boolean { + return isValidSize(this.size.x) && isValidSize(this.size.y); + } + + public get horizontalArea(): number { + return this.size.x * this.size.y; + } + + public get hasVolume(): boolean { + return isValidSize(this.size.x) && isValidSize(this.size.y) && isValidSize(this.size.z); + } + + public get volume(): number { + return this.size.x * this.size.y * this.size.z; + } + + public getBoundingBox(): Box3 { + const matrix = this.getMatrix(); + const boundingBox = new Box3().makeEmpty(); + const unitCube = Range3.createCube(0.5); + const corner = new Vector3(); + for (let i = 0; i < 8; i++) { + unitCube.getCornerPoint(i, corner); + corner.applyMatrix4(matrix); + boundingBox.expandByPoint(corner); + } + return boundingBox; + } + + public getRotationMatrix(matrix: Matrix4 = new Matrix4()): Matrix4 { + matrix.makeRotationZ(this.zRotation); + return matrix; + } + + public getMatrix(matrix: Matrix4 = new Matrix4()): Matrix4 { + return this.getScaledMatrix(this.size, matrix); + } + + public getScaledMatrix(scale: Vector3, matrix: Matrix4 = new Matrix4()): Matrix4 { + matrix = this.getRotationMatrix(matrix); + matrix.setPosition(this.center); + matrix.scale(scale); + return matrix; + } + + // ================================================== + // INSTANCE METHODS: Others + // ================================================== + + public forceMinSize(): void { + const { size } = this; + size.x = Math.max(MIN_BOX_SIZE, size.x); + size.y = Math.max(MIN_BOX_SIZE, size.y); + size.z = Math.max(MIN_BOX_SIZE, size.z); + } + + public setFocusInteractive(focusType: FocusType, focusFace?: BoxFace): boolean { + if (focusType === FocusType.None) { + if (this.focusType === FocusType.None) { + return false; // No change + } + this.focusType = FocusType.None; + this.focusFace = undefined; // Ignore input face + } else { + if (focusType === this.focusType && BoxFace.equals(this.focusFace, focusFace)) { + return false; // No change + } + this.focusType = focusType; + this.focusFace = focusFace; + } + this.notify(Changes.focus); + return true; + } + + public get isUseAsCropBox(): boolean { + const root = this.rootDomainObject; + if (root === undefined) { + return false; + } + return root.renderTarget.isGlobalCropBox(this); + } + + public setUseAsCropBox(use: boolean): void { + const root = this.rootDomainObject; + if (root === undefined) { + return; + } + if (!use) { + root.renderTarget.clearGlobalCropBox(); + } else { + const boundingBox = this.getBoundingBox(); + boundingBox.applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION); + const matrix = this.getMatrix(); + matrix.premultiply(CDF_TO_VIEWER_TRANSFORMATION); + const planes = BoxFace.createClippingPlanes(matrix); + root.renderTarget.setGlobalCropBox(planes, boundingBox, this); + } + } +} + +// ================================================== +// PUBLIC FUNCTIONS +// ================================================== + +export function isValidSize(value: number): boolean { + return value > MIN_BOX_SIZE; +} diff --git a/react-components/src/architecture/concrete/box/LineDomainObject.ts b/react-components/src/architecture/concrete/box/LineDomainObject.ts new file mode 100644 index 00000000000..bc2714c6c7e --- /dev/null +++ b/react-components/src/architecture/concrete/box/LineDomainObject.ts @@ -0,0 +1,205 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; +import { type ThreeView } from '../../base/views/ThreeView'; +import { MeasureLineView } from './MeasureLineView'; +import { Color, Vector3 } from 'three'; +import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; +import { MeasureLineRenderStyle } from './MeasureLineRenderStyle'; +import { + getHorizontalCrossProduct, + horizontalDistanceTo, + verticalDistanceTo +} from '../../base/utilities/extensions/vectorExtensions'; +import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; +import { Changes } from '../../base/domainObjectsHelpers/Changes'; +import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; +import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; +import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; +import { VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; + +export class LineDomainObject extends VisualDomainObject { + // ================================================== + // INSTANCE FIELDS + // ================================================== + + public readonly points: Vector3[] = []; + private readonly _measureType: MeasureType; + public focusType = FocusType.None; + + // ================================================== + // INSTANCE PROPERTIES + // ================================================== + + public get renderStyle(): MeasureLineRenderStyle { + return this.getRenderStyle() as MeasureLineRenderStyle; + } + + public get measureType(): MeasureType { + return this._measureType; + } + + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor(measureType: MeasureType) { + super(); + this._measureType = measureType; + this.color = new Color(Color.NAMES.red); + } + + // ================================================== + // OVERRIDES of DomainObject + // ================================================== + + public override get icon(): string { + return getIconByMeasureType(this.measureType); + } + + public override get typeName(): string { + return getNameByMeasureType(this.measureType); + } + + public override createRenderStyle(): RenderStyle | undefined { + return new MeasureLineRenderStyle(); + } + + public override get hasPanelInfo(): boolean { + return true; + } + + public override getPanelInfoStyle(): PopupStyle { + // bottom = 66 because the toolbar is below + return new PopupStyle({ bottom: 66, left: 0 }); + } + + public override getPanelInfo(): PanelInfo | undefined { + if (this.focusType === FocusType.Pending && this.points.length <= 1) { + return undefined; + } + const info = new PanelInfo(); + switch (this.measureType) { + case MeasureType.Line: + info.setHeader('MEASUREMENTS_LINE', 'Line'); + add('MEASUREMENTS_LENGTH', 'Length', this.getTotalLength()); + add('MEASUREMENTS_HORIZONTAL_LENGTH', 'Horizontal length', this.getHorizontalLength()); + add('MEASUREMENTS_VERTICAL_LENGTH', 'Vertical length', this.getVerticalLength()); + break; + + case MeasureType.Polyline: + info.setHeader('MEASUREMENTS_POLYLINE', 'Polyline'); + add('MEASUREMENTS_TOTAL_LENGTH', 'Total length', this.getTotalLength()); + break; + case MeasureType.Polygon: + info.setHeader('MEASUREMENTS_POLYGON', 'Polygon'); + add('MEASUREMENTS_TOTAL_LENGTH', 'Total length', this.getTotalLength()); + if (this.points.length > 2) { + add( + 'MEASUREMENTS_HORIZONTAL_AREA', + 'Horizontal area', + this.getHorizontalArea(), + Quantity.Area + ); + } + break; + + default: + throw new Error('Unknown MeasureType type'); + } + return info; + + function add(key: string, fallback: string, value: number, quantity = Quantity.Length): void { + info.add({ key, fallback, value, quantity }); + } + } + + // ================================================== + // OVERRIDES of VisualDomainObject + // ================================================== + + protected override createThreeView(): ThreeView | undefined { + return new MeasureLineView(); + } + + // ================================================== + // INSTANCE METHODS + // ================================================== + + public getTotalLength(): number { + let prevPoint: Vector3 | undefined; + let sum = 0.0; + for (const point of this.points) { + if (prevPoint !== undefined) { + sum += point.distanceTo(prevPoint); + } + prevPoint = point; + } + return sum; + } + + public getAverageLength(): number { + const count = this.points.length; + if (count <= 1) { + return 0; + } + return this.getTotalLength() / (count - 1); + } + + public getHorizontalLength(): number { + let prevPoint: Vector3 | undefined; + let sum = 0.0; + for (const point of this.points) { + if (prevPoint !== undefined) { + sum += horizontalDistanceTo(point, prevPoint); + continue; + } + prevPoint = point; + } + return sum; + } + + public getVerticalLength(): number { + let prevPoint: Vector3 | undefined; + let sum = 0.0; + for (const point of this.points) { + if (prevPoint !== undefined) { + sum += verticalDistanceTo(point, prevPoint); + continue; + } + prevPoint = point; + } + return sum; + } + + public getHorizontalArea(): number { + const { points } = this; + const count = points.length; + if (count <= 2) { + return 0; + } + let sum = 0.0; + const first = points[0]; + const p0 = new Vector3(); + const p1 = new Vector3(); + + for (let index = 1; index <= count; index++) { + p1.copy(points[index % count]); + p1.sub(first); // Translate down to first point, to increase acceracy + sum += getHorizontalCrossProduct(p0, p1); + p0.copy(p1); + } + return Math.abs(sum) / 2; + } + + public setFocusInteractive(focusType: FocusType): boolean { + if (this.focusType === focusType) { + return false; + } + this.focusType = focusType; + this.notify(Changes.focus); + return true; + } +} diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts index 699126cc9c1..733861c90a7 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts @@ -105,7 +105,7 @@ export class MeasureBoxCreator extends BaseCreator { if (measureType === MeasureType.VerticalArea) { return point; } - // Recalculate the point anywhy for >= 1 points + // Recalculate the point anyway for >= 1 points // This makes it more natural and you can pick in empty space if (this.notPendingPointCount === 1 || this.notPendingPointCount === 2) { const plane = new Plane().setFromNormalAndCoplanarPoint(UP_VECTOR, this.firstPoint); diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts index 2817436b7a9..14d43827366 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts @@ -10,22 +10,25 @@ import { Box3, Color, Matrix4, Vector3 } from 'three'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { BoxFace } from '../../base/utilities/box/BoxFace'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; -import { MeasureType } from './MeasureType'; +import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; import { type BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; import { type BaseDragger } from '../../base/domainObjectsHelpers/BaseDragger'; import { MeasureBoxDragger } from './MeasureBoxDragger'; -import { MeasureDomainObject } from './MeasureDomainObject'; import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; import { radToDeg } from 'three/src/math/MathUtils.js'; -import { type CreateDraggerProps } from '../../base/domainObjects/VisualDomainObject'; +import { + VisualDomainObject, + type CreateDraggerProps +} from '../../base/domainObjects/VisualDomainObject'; import { Range3 } from '../../base/utilities/geometry/Range3'; import { CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal'; import { type DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; +import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; export const MIN_BOX_SIZE = 0.01; -export class MeasureBoxDomainObject extends MeasureDomainObject { +export class MeasureBoxDomainObject extends VisualDomainObject { // ================================================== // INSTANCE FIELDS // ================================================== @@ -37,21 +40,27 @@ export class MeasureBoxDomainObject extends MeasureDomainObject { // For focus when edit in 3D (Used when isSelected is true only) public focusType: FocusType = FocusType.None; public focusFace: BoxFace | undefined = undefined; + private readonly _measureType: MeasureType; // ================================================== // INSTANCE PROPERTIES // ================================================== - public override get renderStyle(): MeasureBoxRenderStyle { + public get renderStyle(): MeasureBoxRenderStyle { return this.getRenderStyle() as MeasureBoxRenderStyle; } + public get measureType(): MeasureType { + return this._measureType; + } + // ================================================== // CONSTRUCTOR // ================================================== public constructor(measureType: MeasureType) { - super(measureType); + super(); + this._measureType = measureType; this.color = new Color(Color.NAMES.magenta); } @@ -59,6 +68,14 @@ export class MeasureBoxDomainObject extends MeasureDomainObject { // OVERRIDES of DomainObject // ================================================== + public override get icon(): string { + return getIconByMeasureType(this.measureType); + } + + public override get typeName(): string { + return getNameByMeasureType(this.measureType); + } + public override createRenderStyle(): RenderStyle | undefined { return new MeasureBoxRenderStyle(); } @@ -90,6 +107,15 @@ export class MeasureBoxDomainObject extends MeasureDomainObject { // } } + public override get hasPanelInfo(): boolean { + return true; + } + + public override getPanelInfoStyle(): PopupStyle { + // bottom = 66 because the toolbar is below + return new PopupStyle({ bottom: 66, left: 0 }); + } + public override getPanelInfo(): PanelInfo | undefined { const info = new PanelInfo(); const { measureType } = this; diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxRenderStyle.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxRenderStyle.ts index d74a4af16eb..a9d06c0f55b 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxRenderStyle.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxRenderStyle.ts @@ -3,10 +3,10 @@ */ import { cloneDeep } from 'lodash'; -import { MeasureRenderStyle } from './MeasureRenderStyle'; +import { TextRenderStyle } from './TextRenderStyle'; import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; -export class MeasureBoxRenderStyle extends MeasureRenderStyle { +export class MeasureBoxRenderStyle extends TextRenderStyle { // ================================================== // INSTANCE FIELDS // ================================================== diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxView.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxView.ts index 914b29e04ea..33c18d94d9b 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxView.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxView.ts @@ -45,7 +45,6 @@ import { BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; import { radToDeg } from 'three/src/math/MathUtils.js'; import { Range1 } from '../../base/utilities/geometry/Range1'; import { MeasureType } from './MeasureType'; -import { type MeasureRenderStyle } from './MeasureRenderStyle'; import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; const RELATIVE_RESIZE_RADIUS = 0.15; @@ -623,7 +622,11 @@ function updateMarkerMaterial( // PRIVATE FUNCTIONS: Create object3D's // ================================================== -function createSprite(text: string, style: MeasureRenderStyle, height: number): Sprite | undefined { +function createSprite( + text: string, + style: MeasureBoxRenderStyle, + height: number +): Sprite | undefined { const result = createSpriteWithText(text, height, style.textColor, style.textBgColor); if (result === undefined) { return undefined; diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureDomainObject.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureDomainObject.ts deleted file mode 100644 index ab40a47703f..00000000000 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureDomainObject.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*! - * Copyright 2024 Cognite AS - */ - -import { VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; -import { getIconByMeasureType, getNameByMeasureType, type MeasureType } from './MeasureType'; -import { type MeasureRenderStyle } from './MeasureRenderStyle'; -import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; - -export abstract class MeasureDomainObject extends VisualDomainObject { - private readonly _measureType: MeasureType; - - // ================================================== - // INSTANCE PROPERTIES - // ================================================== - - public get renderStyle(): MeasureRenderStyle { - return this.getRenderStyle() as MeasureRenderStyle; - } - - public get measureType(): MeasureType { - return this._measureType; - } - - // ================================================== - // CONSTRUCTOR - // ================================================== - - public constructor(measureType: MeasureType) { - super(); - this._measureType = measureType; - } - - // ================================================== - // OVERRIDES - // ================================================== - - public override get icon(): string { - return getIconByMeasureType(this.measureType); - } - - public override get typeName(): string { - return getNameByMeasureType(this.measureType); - } - - public override get hasPanelInfo(): boolean { - return true; - } - - public override getPanelInfoStyle(): PopupStyle { - // bottom = 66 because the toolbar is below - return new PopupStyle({ bottom: 66, left: 0 }); - } -} diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts index 746e6ab3fd8..d4de03e7fe2 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts @@ -6,9 +6,8 @@ import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; import { type ThreeView } from '../../base/views/ThreeView'; import { MeasureLineView } from './MeasureLineView'; import { Color, Vector3 } from 'three'; -import { MeasureType } from './MeasureType'; +import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; import { MeasureLineRenderStyle } from './MeasureLineRenderStyle'; -import { MeasureDomainObject } from './MeasureDomainObject'; import { getHorizontalCrossProduct, horizontalDistanceTo, @@ -18,29 +17,37 @@ import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; +import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; +import { VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; -export class MeasureLineDomainObject extends MeasureDomainObject { +export class MeasureLineDomainObject extends VisualDomainObject { // ================================================== // INSTANCE FIELDS // ================================================== public readonly points: Vector3[] = []; + private readonly _measureType: MeasureType; public focusType = FocusType.None; // ================================================== // INSTANCE PROPERTIES // ================================================== - public override get renderStyle(): MeasureLineRenderStyle { + public get renderStyle(): MeasureLineRenderStyle { return this.getRenderStyle() as MeasureLineRenderStyle; } + public get measureType(): MeasureType { + return this._measureType; + } + // ================================================== // CONSTRUCTOR // ================================================== public constructor(measureType: MeasureType) { - super(measureType); + super(); + this._measureType = measureType; this.color = new Color(Color.NAMES.red); } @@ -48,10 +55,27 @@ export class MeasureLineDomainObject extends MeasureDomainObject { // OVERRIDES of DomainObject // ================================================== + public override get icon(): string { + return getIconByMeasureType(this.measureType); + } + + public override get typeName(): string { + return getNameByMeasureType(this.measureType); + } + public override createRenderStyle(): RenderStyle | undefined { return new MeasureLineRenderStyle(); } + public override get hasPanelInfo(): boolean { + return true; + } + + public override getPanelInfoStyle(): PopupStyle { + // bottom = 66 because the toolbar is below + return new PopupStyle({ bottom: 66, left: 0 }); + } + public override getPanelInfo(): PanelInfo | undefined { if (this.focusType === FocusType.Pending && this.points.length <= 1) { return undefined; diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineRenderStyle.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineRenderStyle.ts index 5a6765943a7..fa01d5c316d 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineRenderStyle.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineRenderStyle.ts @@ -3,10 +3,10 @@ */ import { cloneDeep } from 'lodash'; -import { MeasureRenderStyle } from './MeasureRenderStyle'; +import { TextRenderStyle } from './TextRenderStyle'; import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; -export class MeasureLineRenderStyle extends MeasureRenderStyle { +export class MeasureLineRenderStyle extends TextRenderStyle { // ================================================== // INSTANCE FIELDS // ================================================== diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineView.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineView.ts index eba2613e3f5..a78445f150c 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineView.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineView.ts @@ -34,7 +34,6 @@ import { MeasureType } from './MeasureType'; import { createSpriteWithText } from '../../base/utilities/sprites/createSprite'; import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; -import { MeasureRenderStyle } from './MeasureRenderStyle'; import { DomainObjectIntersection } from '../../base/domainObjectsHelpers/DomainObjectIntersection'; import { ClosestGeometryFinder } from '../../base/utilities/geometry/ClosestGeometryFinder'; import { square } from '../../base/utilities/extensions/mathExtensions'; @@ -313,7 +312,11 @@ function createVertices(domainObject: MeasureLineDomainObject): number[] | undef return vertices; } -function createSprite(text: string, style: MeasureRenderStyle, height: number): Sprite | undefined { +function createSprite( + text: string, + style: MeasureLineRenderStyle, + height: number +): Sprite | undefined { const result = createSpriteWithText(text, height, style.textColor, style.textBgColor); if (result === undefined) { return undefined; diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts index 4ebeee6c2ff..bf9473d9d96 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts @@ -15,9 +15,7 @@ import { type BaseCreator } from '../../base/domainObjectsHelpers/BaseCreator'; import { MeasureLineCreator } from './MeasureLineCreator'; import { BaseEditTool } from '../../base/commands/BaseEditTool'; import { MeasureLineDomainObject } from './MeasureLineDomainObject'; -import { MeasureRenderStyle } from './MeasureRenderStyle'; import { type DomainObject } from '../../base/domainObjects/DomainObject'; -import { MeasureDomainObject } from './MeasureDomainObject'; import { ShowMeasurementsOnTopCommand } from './ShowMeasurementsOnTopCommand'; import { SetMeasurementTypeCommand } from './SetMeasurementTypeCommand'; import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; @@ -25,6 +23,8 @@ import { type RootDomainObject } from '../../base/domainObjects/RootDomainObject import { CommandsUpdater } from '../../base/reactUpdaters/CommandsUpdater'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; import { ToggleMetricUnitsCommand } from '../../base/concreteCommands/ToggleMetricUnitsCommand'; +import { MeasureBoxRenderStyle } from './MeasureBoxRenderStyle'; +import { type VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; export class MeasurementTool extends BaseEditTool { // ================================================== @@ -229,7 +229,10 @@ export class MeasurementTool extends BaseEditTool { // ================================================== protected override canBeSelected(domainObject: DomainObject): boolean { - return domainObject instanceof MeasureDomainObject; + return ( + domainObject instanceof MeasureBoxDomainObject || + domainObject instanceof MeasureLineDomainObject + ); } // ================================================== @@ -249,22 +252,23 @@ export class MeasurementTool extends BaseEditTool { } private setAllMeasurementsVisible(visible: boolean): void { - for (const domainObject of this.rootDomainObject.getDescendantsByType(MeasureDomainObject)) { - domainObject.setVisibleInteractive(visible, this.renderTarget); + for (const domainObject of this.rootDomainObject.getDescendants()) { + if (this.canBeSelected(domainObject)) { + domainObject.setVisibleInteractive(visible, this.renderTarget); + } } } private getMeasurement( intersection: AnyIntersection | undefined - ): MeasureDomainObject | undefined { + ): VisualDomainObject | undefined { if (!isDomainObjectIntersection(intersection)) { return undefined; } - if (intersection.domainObject instanceof MeasureDomainObject) { - return intersection.domainObject; - } else { + if (!this.canBeSelected(intersection.domainObject)) { return undefined; } + return intersection.domainObject as VisualDomainObject; } private setCursor( @@ -306,7 +310,7 @@ export class MeasurementTool extends BaseEditTool { } protected defocusAll(except?: DomainObject | undefined): void { - for (const domainObject of this.rootDomainObject.getDescendantsByType(MeasureDomainObject)) { + for (const domainObject of this.rootDomainObject.getDescendants()) { if (except !== undefined && domainObject === except) { continue; } @@ -325,17 +329,22 @@ export class MeasurementTool extends BaseEditTool { // ================================================== function initializeStyle(domainObject: DomainObject, rootDomainObject: RootDomainObject): void { - // Just copy the style the depthTest field from any other MeasureDomainObject - const otherDomainObject = rootDomainObject.getDescendantByType(MeasureDomainObject); - if (otherDomainObject === undefined) { + // Just copy the style the depthTest field from any other measure DomainObject + const style = domainObject.getRenderStyle(); + if (!(style instanceof MeasureBoxRenderStyle)) { return; } - const otherStyle = otherDomainObject.renderStyle; - const style = domainObject.getRenderStyle(); - if (!(style instanceof MeasureRenderStyle)) { + const otherBoxDomainObject = rootDomainObject.getDescendantByType(MeasureBoxDomainObject); + if (otherBoxDomainObject !== undefined) { + const otherStyle = otherBoxDomainObject.renderStyle; + style.depthTest = otherStyle.depthTest; return; } - style.depthTest = otherStyle.depthTest; + const otherLineDomainObject = rootDomainObject.getDescendantByType(MeasureLineDomainObject); + if (otherLineDomainObject !== undefined) { + const otherStyle = otherLineDomainObject.renderStyle; + style.depthTest = otherStyle.depthTest; + } } function createCreator(measureType: MeasureType): BaseCreator | undefined { diff --git a/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts b/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts index 741b6d17a92..8e3ddfcb1df 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts @@ -5,7 +5,8 @@ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; -import { MeasureDomainObject } from './MeasureDomainObject'; +import { MeasureBoxDomainObject } from './MeasureBoxDomainObject'; +import { MeasureLineDomainObject } from './MeasureLineDomainObject'; export class ShowMeasurementsOnTopCommand extends RenderTargetCommand { // ================================================== @@ -30,10 +31,16 @@ export class ShowMeasurementsOnTopCommand extends RenderTargetCommand { protected override invokeCore(): boolean { const depthTest = this.getDepthTest(); - for (const domainObject of this.rootDomainObject.getDescendantsByType(MeasureDomainObject)) { - const style = domainObject.renderStyle; - style.depthTest = !depthTest; - domainObject.notify(Changes.renderStyle); + for (const domainObject of this.rootDomainObject.getDescendants()) { + if (domainObject instanceof MeasureBoxDomainObject) { + const style = domainObject.renderStyle; + style.depthTest = !depthTest; + domainObject.notify(Changes.renderStyle); + } else if (domainObject instanceof MeasureLineDomainObject) { + const style = domainObject.renderStyle; + style.depthTest = !depthTest; + domainObject.notify(Changes.renderStyle); + } } return true; } @@ -51,7 +58,15 @@ export class ShowMeasurementsOnTopCommand extends RenderTargetCommand { return style.depthTest; } - private getFirst(): MeasureDomainObject | undefined { - return this.rootDomainObject.getDescendantByType(MeasureDomainObject); + private getFirst(): MeasureBoxDomainObject | MeasureLineDomainObject | undefined { + for (const domainObject of this.rootDomainObject.getDescendants()) { + if (domainObject instanceof MeasureBoxDomainObject) { + return domainObject; + } + if (domainObject instanceof MeasureLineDomainObject) { + return domainObject; + } + } + return undefined; } } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureRenderStyle.ts b/react-components/src/architecture/concrete/boxDomainObject/TextRenderStyle.ts similarity index 92% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureRenderStyle.ts rename to react-components/src/architecture/concrete/boxDomainObject/TextRenderStyle.ts index 5fd0bab3f0e..ebf007ce575 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureRenderStyle.ts +++ b/react-components/src/architecture/concrete/boxDomainObject/TextRenderStyle.ts @@ -7,7 +7,7 @@ import { RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; import { Color } from 'three'; import { WHITE_COLOR } from '../../base/utilities/colors/colorExtensions'; -export abstract class MeasureRenderStyle extends RenderStyle { +export abstract class TextRenderStyle extends RenderStyle { // ================================================== // INSTANCE FIELDS // ================================================== From 9ace9a238d302236a79696df57a25d421d863c3e Mon Sep 17 00:00:00 2001 From: Nils Petter Fremming Date: Wed, 12 Jun 2024 00:29:27 +0200 Subject: [PATCH 02/37] Generalized box and lines --- .../architecture/base/commands/BaseCommand.ts | 18 +- .../base/commands/BaseEditTool.ts | 58 ++-- .../base/domainObjects/DomainObject.ts | 17 + .../base/renderTarget/RevealRenderTarget.ts | 8 +- .../BoxCreator.ts} | 38 +-- .../concrete/box/BoxDomainObject.ts | 115 ++----- .../BoxDragger.ts} | 12 +- .../BoxOrLineEditTool.ts} | 187 +++-------- .../BoxRenderStyle.ts} | 4 +- .../MeasureBoxView.ts => box/BoxView.ts} | 48 ++- .../LineCreator.ts} | 24 +- .../concrete/box/LineDomainObject.ts | 49 ++- .../LineRenderStyle.ts} | 4 +- .../MeasureLineView.ts => box/LineView.ts} | 44 ++- .../concrete/box/PrimitiveType.ts | 13 + .../TextRenderStyle.ts | 2 +- .../boxDomainObject/MeasureBoxDomainObject.ts | 314 ------------------ .../MeasureLineDomainObject.ts | 205 ------------ .../concrete/boxDomainObject/MeasureType.ts | 92 ----- .../SetMeasurementTypeCommand.ts | 80 ----- .../concrete/config/StoryBookConfig.ts | 4 +- .../concrete/cropping/CropBoxDomainObject.ts | 81 +++++ .../concrete/cropping/CropBoxTool.ts | 72 ++++ .../SetCropBoxCommand.ts | 20 +- .../measurments/MeasureBoxDomainObject.ts | 42 +++ .../measurments/MeasureLineDomainObject.ts | 40 +++ .../concrete/measurments/MeasurementTool.ts | 98 ++++++ .../measurments/SetMeasurementTypeCommand.ts | 124 +++++++ .../ShowMeasurementsOnTopCommand.ts | 0 .../measurments/getIconByPrimitiveType.ts | 24 ++ .../components/Architecture/CommandButton.tsx | 10 +- .../components/Architecture/RevealButtons.tsx | 2 +- 32 files changed, 757 insertions(+), 1092 deletions(-) rename react-components/src/architecture/concrete/{boxDomainObject/MeasureBoxCreator.ts => box/BoxCreator.ts} (83%) rename react-components/src/architecture/concrete/{boxDomainObject/MeasureBoxDragger.ts => box/BoxDragger.ts} (95%) rename react-components/src/architecture/concrete/{boxDomainObject/MeasurementTool.ts => box/BoxOrLineEditTool.ts} (56%) rename react-components/src/architecture/concrete/{boxDomainObject/MeasureBoxRenderStyle.ts => box/BoxRenderStyle.ts} (83%) rename react-components/src/architecture/concrete/{boxDomainObject/MeasureBoxView.ts => box/BoxView.ts} (95%) rename react-components/src/architecture/concrete/{boxDomainObject/MeasureLineCreator.ts => box/LineCreator.ts} (81%) rename react-components/src/architecture/concrete/{boxDomainObject/MeasureLineRenderStyle.ts => box/LineRenderStyle.ts} (85%) rename react-components/src/architecture/concrete/{boxDomainObject/MeasureLineView.ts => box/LineView.ts} (88%) create mode 100644 react-components/src/architecture/concrete/box/PrimitiveType.ts rename react-components/src/architecture/concrete/{boxDomainObject => box}/TextRenderStyle.ts (90%) delete mode 100644 react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts delete mode 100644 react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts delete mode 100644 react-components/src/architecture/concrete/boxDomainObject/MeasureType.ts delete mode 100644 react-components/src/architecture/concrete/boxDomainObject/SetMeasurementTypeCommand.ts create mode 100644 react-components/src/architecture/concrete/cropping/CropBoxDomainObject.ts create mode 100644 react-components/src/architecture/concrete/cropping/CropBoxTool.ts rename react-components/src/architecture/concrete/{boxDomainObject => cropping}/SetCropBoxCommand.ts (68%) create mode 100644 react-components/src/architecture/concrete/measurments/MeasureBoxDomainObject.ts create mode 100644 react-components/src/architecture/concrete/measurments/MeasureLineDomainObject.ts create mode 100644 react-components/src/architecture/concrete/measurments/MeasurementTool.ts create mode 100644 react-components/src/architecture/concrete/measurments/SetMeasurementTypeCommand.ts rename react-components/src/architecture/concrete/{boxDomainObject => measurments}/ShowMeasurementsOnTopCommand.ts (100%) create mode 100644 react-components/src/architecture/concrete/measurments/getIconByPrimitiveType.ts diff --git a/react-components/src/architecture/base/commands/BaseCommand.ts b/react-components/src/architecture/base/commands/BaseCommand.ts index d3fe91c0ff7..9f973074242 100644 --- a/react-components/src/architecture/base/commands/BaseCommand.ts +++ b/react-components/src/architecture/base/commands/BaseCommand.ts @@ -22,23 +22,27 @@ export abstract class BaseCommand { private readonly _listeners: UpdateDelegate[] = []; - // Unique index for the command, used by in React to force rerender + // Unique id for the command, used by in React to force rerender // when the command changes for a button. - public readonly _uniqueIndex: number; + private readonly _uniqueId: number; - public get uniqueIndex(): number { - return this._uniqueIndex; + public get uniqueId(): number { + return this._uniqueId; } // ================================================== - // VIRTUAL METHODS (To be override) - // ================================================= + // CONSTRUCTOR + // ================================================== constructor() { BaseCommand._counter++; - this._uniqueIndex = BaseCommand._counter; + this._uniqueId = BaseCommand._counter; } + // ================================================== + // VIRTUAL METHODS (To be override) + // ================================================= + public get name(): string { return this.tooltip.fallback ?? this.tooltip.key; } diff --git a/react-components/src/architecture/base/commands/BaseEditTool.ts b/react-components/src/architecture/base/commands/BaseEditTool.ts index 6094b0d11dd..550eb58f6cf 100644 --- a/react-components/src/architecture/base/commands/BaseEditTool.ts +++ b/react-components/src/architecture/base/commands/BaseEditTool.ts @@ -111,22 +111,29 @@ 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 { + protected *getSelectable(): Generator { const { rootDomainObject } = this; for (const domainObject of rootDomainObject.getDescendantsByType(VisualDomainObject)) { if (!this.canBeSelected(domainObject)) { continue; } - if (except !== undefined && domainObject === except) { + yield 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(): VisualDomainObject | undefined { + for (const domainObject of this.getSelectable()) { + if (!domainObject.isSelected) { continue; } - domainObject.setSelectedInteractive(false); + return domainObject; } + return undefined; } /** @@ -135,35 +142,36 @@ export abstract class BaseEditTool extends NavigationTool { * @returns A generator that yields each selected domain object. */ protected *getAllSelected(): Generator { - const { rootDomainObject } = this; - for (const domainObject of rootDomainObject.getDescendantsByType(VisualDomainObject)) { + for (const domainObject of this.getSelectable()) { if (!domainObject.isSelected) { continue; } - if (!this.canBeSelected(domainObject)) { - continue; - } yield domainObject; } } /** - * Retrieves the selected VisualDomainObject. - * Use only if single selection is expected. - * @returns The selected DomainObject, or undefined if no object is selected. + * Deselects all selectable 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 getSelected(): VisualDomainObject | undefined { - const { rootDomainObject } = this; - for (const domainObject of rootDomainObject.getDescendantsByType(VisualDomainObject)) { - if (!domainObject.isSelected) { - continue; - } - if (!this.canBeSelected(domainObject)) { + protected deselectAll(except?: VisualDomainObject | undefined): void { + for (const domainObject of this.getSelectable()) { + if (except !== undefined && domainObject === except) { continue; } - return domainObject; + domainObject.setSelectedInteractive(false); + } + } + + /** + * Sets the visibility of all selectable objects. + * @param visible - A boolean indicating whether the objects should be visible or not. + */ + protected setAllVisible(visible: boolean): void { + for (const domainObject of this.getSelectable()) { + domainObject.setVisibleInteractive(visible, this.renderTarget); } - return undefined; } /** diff --git a/react-components/src/architecture/base/domainObjects/DomainObject.ts b/react-components/src/architecture/base/domainObjects/DomainObject.ts index 65749b0ca7e..a3571a89839 100644 --- a/react-components/src/architecture/base/domainObjects/DomainObject.ts +++ b/react-components/src/architecture/base/domainObjects/DomainObject.ts @@ -55,6 +55,23 @@ export abstract class DomainObject { // Views and listeners public readonly views: Views = new Views(); + // Unique index for the domain object, used as soft reference + private readonly _uniqueId: number; + private static _counter: number = 0; // Counter for the unique index + + public get uniqueId(): number { + return this._uniqueId; + } + + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor() { + DomainObject._counter++; + this._uniqueId = DomainObject._counter; + } + // ================================================== // INSTANCE/VIRTUAL PROPERTIES // ================================================== diff --git a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts index f1ad5caf8fe..45cf3a7c2ba 100644 --- a/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts +++ b/react-components/src/architecture/base/renderTarget/RevealRenderTarget.ts @@ -40,7 +40,7 @@ export class RevealRenderTarget { private _ambientLight: AmbientLight | undefined; private _directionalLight: DirectionalLight | undefined; private _cropBoxBoundingBox: Box3 | undefined; - private _cropBoxName: string | undefined = undefined; + private _cropBoxUniqueId: number | undefined = undefined; private _axisGizmoTool: AxisGizmoTool | undefined; private _config: BaseRevealConfig | undefined = undefined; @@ -225,17 +225,17 @@ export class RevealRenderTarget { // Input in Viewer coordinates this.viewer.setGlobalClippingPlanes(clippingPlanes); this._cropBoxBoundingBox = boundingBox; - this._cropBoxName = domainObject.name; + this._cropBoxUniqueId = domainObject.uniqueId; } public clearGlobalCropBox(): void { this.viewer.setGlobalClippingPlanes([]); this._cropBoxBoundingBox = undefined; - this._cropBoxName = undefined; + this._cropBoxUniqueId = undefined; } public isGlobalCropBox(domainObject: DomainObject): boolean { - return this._cropBoxName !== undefined && domainObject.hasEqualName(this._cropBoxName); + return this._cropBoxUniqueId !== undefined && domainObject.uniqueId === this._cropBoxUniqueId; } public get isGlobalCropBoxActive(): boolean { diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts b/react-components/src/architecture/concrete/box/BoxCreator.ts similarity index 83% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts rename to react-components/src/architecture/concrete/box/BoxCreator.ts index 733861c90a7..98a9300e477 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxCreator.ts +++ b/react-components/src/architecture/concrete/box/BoxCreator.ts @@ -9,32 +9,32 @@ import { } from '../../base/utilities/extensions/vectorExtensions'; import { Range3 } from '../../base/utilities/geometry/Range3'; import { forceBetween0AndPi } from '../../base/utilities/extensions/mathExtensions'; -import { MeasureBoxDomainObject } from './MeasureBoxDomainObject'; -import { MeasureType } from './MeasureType'; +import { PrimitiveType } from './PrimitiveType'; import { getClosestPointOnLine } from '../../base/utilities/extensions/rayExtensions'; import { BaseCreator } from '../../base/domainObjectsHelpers/BaseCreator'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { type DomainObject } from '../../base/domainObjects/DomainObject'; +import { type BoxDomainObject } from './BoxDomainObject'; const UP_VECTOR = new Vector3(0, 0, 1); /** - * Helper class for generate a MeasureBoxDomainObject by clicking around + * Helper class for generate a BoxDomainObject by clicking around */ -export class MeasureBoxCreator extends BaseCreator { +export class BoxCreator extends BaseCreator { // ================================================== // INSTANCE FIELDS // ================================================== - private readonly _domainObject: MeasureBoxDomainObject; + private readonly _domainObject: BoxDomainObject; // ================================================== // CONSTRUCTOR // ================================================== - constructor(measureType: MeasureType) { + constructor(domainObject: BoxDomainObject) { super(); - this._domainObject = new MeasureBoxDomainObject(measureType); + this._domainObject = domainObject; this._domainObject.focusType = FocusType.Pending; } @@ -51,15 +51,15 @@ export class MeasureBoxCreator extends BaseCreator { } public override get maximumPointCount(): number { - switch (this._domainObject.measureType) { - case MeasureType.VerticalArea: + switch (this._domainObject.primitiveType) { + case PrimitiveType.VerticalArea: return 2; - case MeasureType.HorizontalArea: + case PrimitiveType.HorizontalArea: return 3; - case MeasureType.Volume: + case PrimitiveType.Box: return 4; default: - throw new Error('Unknown measurement type'); + throw new Error('Unknown primitiveType'); } } @@ -69,7 +69,7 @@ export class MeasureBoxCreator extends BaseCreator { isPending: boolean ): boolean { const domainObject = this._domainObject; - point = this.recalculatePoint(point, ray, domainObject.measureType); + point = this.recalculatePoint(point, ray, domainObject.primitiveType); if (point === undefined) { return false; } @@ -100,9 +100,9 @@ export class MeasureBoxCreator extends BaseCreator { private recalculatePoint( point: Vector3 | undefined, ray: Ray, - measureType: MeasureType + primitiveType: PrimitiveType ): Vector3 | undefined { - if (measureType === MeasureType.VerticalArea) { + if (primitiveType === PrimitiveType.VerticalArea) { return point; } // Recalculate the point anyway for >= 1 points @@ -111,7 +111,7 @@ export class MeasureBoxCreator extends BaseCreator { const plane = new Plane().setFromNormalAndCoplanarPoint(UP_VECTOR, this.firstPoint); const newPoint = ray.intersectPlane(plane, new Vector3()); return newPoint ?? undefined; - } else if (this.notPendingPointCount === 3 && measureType === MeasureType.Volume) { + } else if (this.notPendingPointCount === 3 && primitiveType === PrimitiveType.Box) { return getClosestPointOnLine(ray, UP_VECTOR, this.points[2], point); } return point; @@ -132,7 +132,7 @@ export class MeasureBoxCreator extends BaseCreator { if (this.pointCount === 1) { domainObject.forceMinSize(); domainObject.center.copy(this.firstPoint); - if (domainObject.measureType !== MeasureType.VerticalArea) { + if (domainObject.primitiveType !== PrimitiveType.VerticalArea) { domainObject.center.z += domainObject.size.z / 2; } return true; @@ -142,7 +142,7 @@ export class MeasureBoxCreator extends BaseCreator { const vector = new Vector3().subVectors(this.firstPoint, this.lastPoint); domainObject.zRotation = forceBetween0AndPi(horizontalAngle(vector)); } - const measureType = domainObject.measureType; + const primitiveType = domainObject.primitiveType; if (this.pointCount <= 3) { // Set the center and the size only in 2D space const newCenter = new Vector3(); @@ -152,7 +152,7 @@ export class MeasureBoxCreator extends BaseCreator { domainObject.center.x = newCenter.x; domainObject.size.x = newSize.x; - if (measureType === MeasureType.VerticalArea) { + if (primitiveType === PrimitiveType.VerticalArea) { domainObject.center.z = newCenter.z; domainObject.center.y = newCenter.y; domainObject.size.z = newSize.z; diff --git a/react-components/src/architecture/concrete/box/BoxDomainObject.ts b/react-components/src/architecture/concrete/box/BoxDomainObject.ts index 4f1bc5cd8c4..16855774776 100644 --- a/react-components/src/architecture/concrete/box/BoxDomainObject.ts +++ b/react-components/src/architecture/concrete/box/BoxDomainObject.ts @@ -2,18 +2,18 @@ * Copyright 2024 Cognite AS */ -import { MeasureBoxRenderStyle } from './MeasureBoxRenderStyle'; +import { BoxRenderStyle } from './BoxRenderStyle'; import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; import { type ThreeView } from '../../base/views/ThreeView'; -import { MeasureBoxView } from './MeasureBoxView'; -import { Box3, Color, Matrix4, Vector3 } from 'three'; +import { BoxView } from './BoxView'; +import { Box3, Matrix4, Vector3 } from 'three'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { BoxFace } from '../../base/utilities/box/BoxFace'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; -import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; +import { PrimitiveType } from './PrimitiveType'; import { type BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; import { type BaseDragger } from '../../base/domainObjectsHelpers/BaseDragger'; -import { MeasureBoxDragger } from './MeasureBoxDragger'; +import { BoxDragger } from './BoxDragger'; import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; import { radToDeg } from 'three/src/math/MathUtils.js'; import { @@ -21,14 +21,12 @@ import { type CreateDraggerProps } from '../../base/domainObjects/VisualDomainObject'; import { Range3 } from '../../base/utilities/geometry/Range3'; -import { CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal'; -import { type DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; export const MIN_BOX_SIZE = 0.01; -export class BoxDomainObject extends VisualDomainObject { +export abstract class BoxDomainObject extends VisualDomainObject { // ================================================== // INSTANCE FIELDS // ================================================== @@ -40,44 +38,35 @@ export class BoxDomainObject extends VisualDomainObject { // For focus when edit in 3D (Used when isSelected is true only) public focusType: FocusType = FocusType.None; public focusFace: BoxFace | undefined = undefined; - private readonly _measureType: MeasureType; + private readonly _primitiveType: PrimitiveType; // ================================================== // INSTANCE PROPERTIES // ================================================== - public get renderStyle(): MeasureBoxRenderStyle { - return this.getRenderStyle() as MeasureBoxRenderStyle; + public get renderStyle(): BoxRenderStyle { + return this.getRenderStyle() as BoxRenderStyle; } - public get measureType(): MeasureType { - return this._measureType; + public get primitiveType(): PrimitiveType { + return this._primitiveType; } // ================================================== // CONSTRUCTOR // ================================================== - public constructor(measureType: MeasureType) { + protected constructor(primitiveType: PrimitiveType = PrimitiveType.Box) { super(); - this._measureType = measureType; - this.color = new Color(Color.NAMES.magenta); + this._primitiveType = primitiveType; } // ================================================== // OVERRIDES of DomainObject // ================================================== - public override get icon(): string { - return getIconByMeasureType(this.measureType); - } - - public override get typeName(): string { - return getNameByMeasureType(this.measureType); - } - public override createRenderStyle(): RenderStyle | undefined { - return new MeasureBoxRenderStyle(); + return new BoxRenderStyle(); } public override createDragger(props: CreateDraggerProps): BaseDragger | undefined { @@ -85,26 +74,7 @@ export class BoxDomainObject extends VisualDomainObject { if (pickInfo === undefined) { return undefined; // If the BoxPickInfo isn't specified, no dragger is created } - return new MeasureBoxDragger(props, this); - } - - protected override notifyCore(change: DomainObjectChange): void { - super.notifyCore(change); - - // Experimental code for crop box (let it stay here) - // if (this.isUseAsCropBox) { - // if (change.isChanged(Changes.deleted)) { - // this.setUseAsCropBox(false); - // } - // if (change.isChanged(Changes.geometry)) { - // this.setUseAsCropBox(true); - // } - // } else if (change.isChanged(Changes.selected) && this.isSelected) { - // const root = this.root as RootDomainObject; - // if (root instanceof RootDomainObject && root.renderTarget.isGlobalCropBoxActive) { - // this.setUseAsCropBox(true); - // } - // } + return new BoxDragger(props, this); } public override get hasPanelInfo(): boolean { @@ -118,36 +88,36 @@ export class BoxDomainObject extends VisualDomainObject { public override getPanelInfo(): PanelInfo | undefined { const info = new PanelInfo(); - const { measureType } = this; + const { primitiveType: type } = this; const isFinished = this.focusType !== FocusType.Pending; - switch (measureType) { - case MeasureType.HorizontalArea: + switch (type) { + case PrimitiveType.HorizontalArea: info.setHeader('MEASUREMENTS_HORIZONTAL_AREA', 'Horizontal area'); break; - case MeasureType.VerticalArea: + case PrimitiveType.VerticalArea: info.setHeader('MEASUREMENTS_VERTICAL_AREA', 'Vertical area'); break; - case MeasureType.Volume: + case PrimitiveType.Box: info.setHeader('MEASUREMENTS_VOLUME', 'Volume'); break; } if (isFinished || isValidSize(this.size.x)) { add('MEASUREMENTS_LENGTH', 'Length', this.size.x, Quantity.Length); } - if (measureType !== MeasureType.VerticalArea && (isFinished || isValidSize(this.size.y))) { + if (type !== PrimitiveType.VerticalArea && (isFinished || isValidSize(this.size.y))) { add('MEASUREMENTS_DEPTH', 'Depth', this.size.y, Quantity.Length); } - if (measureType !== MeasureType.HorizontalArea && (isFinished || isValidSize(this.size.z))) { + if (type !== PrimitiveType.HorizontalArea && (isFinished || isValidSize(this.size.z))) { add('MEASUREMENTS_HEIGHT', 'Height', this.size.z, Quantity.Length); } - if (measureType !== MeasureType.Volume && (isFinished || this.hasArea)) { + if (type !== PrimitiveType.Box && (isFinished || this.hasArea)) { add('MEASUREMENTS_AREA', 'Area', this.area, Quantity.Area); } - if (measureType === MeasureType.Volume && (isFinished || this.hasHorizontalArea)) { + if (type === PrimitiveType.Box && (isFinished || this.hasHorizontalArea)) { add('MEASUREMENTS_HORIZONTAL_AREA', 'Horizontal area', this.horizontalArea, Quantity.Area); } - if (measureType === MeasureType.Volume && (isFinished || this.hasVolume)) { + if (type === PrimitiveType.Box && (isFinished || this.hasVolume)) { add('MEASUREMENTS_VOLUME', 'Volume', this.volume, Quantity.Volume); } // I forgot to add text for rotation angle before the deadline, so I used a icon instead. @@ -171,7 +141,7 @@ export class BoxDomainObject extends VisualDomainObject { // ================================================== protected override createThreeView(): ThreeView | undefined { - return new MeasureBoxView(); + return new BoxView(); } // ================================================== @@ -191,12 +161,12 @@ export class BoxDomainObject extends VisualDomainObject { } public get area(): number { - switch (this.measureType) { - case MeasureType.HorizontalArea: + switch (this.primitiveType) { + case PrimitiveType.HorizontalArea: return this.size.x * this.size.y; - case MeasureType.VerticalArea: + case PrimitiveType.VerticalArea: return this.size.x * this.size.z; - case MeasureType.Volume: { + case PrimitiveType.Box: { const a = this.size.x * this.size.y + this.size.y * this.size.z + this.size.z * this.size.x; return a * 2; } @@ -278,31 +248,6 @@ export class BoxDomainObject extends VisualDomainObject { this.notify(Changes.focus); return true; } - - public get isUseAsCropBox(): boolean { - const root = this.rootDomainObject; - if (root === undefined) { - return false; - } - return root.renderTarget.isGlobalCropBox(this); - } - - public setUseAsCropBox(use: boolean): void { - const root = this.rootDomainObject; - if (root === undefined) { - return; - } - if (!use) { - root.renderTarget.clearGlobalCropBox(); - } else { - const boundingBox = this.getBoundingBox(); - boundingBox.applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION); - const matrix = this.getMatrix(); - matrix.premultiply(CDF_TO_VIEWER_TRANSFORMATION); - const planes = BoxFace.createClippingPlanes(matrix); - root.renderTarget.setGlobalCropBox(planes, boundingBox, this); - } - } } // ================================================== diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDragger.ts b/react-components/src/architecture/concrete/box/BoxDragger.ts similarity index 95% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDragger.ts rename to react-components/src/architecture/concrete/box/BoxDragger.ts index 4fc1a7ece99..099c96b4ee6 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDragger.ts +++ b/react-components/src/architecture/concrete/box/BoxDragger.ts @@ -9,9 +9,9 @@ import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; import { type BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; import { forceBetween0AndPi } from '../../base/utilities/extensions/mathExtensions'; import { horizontalAngle } from '../../base/utilities/extensions/vectorExtensions'; -import { MeasureType } from './MeasureType'; +import { PrimitiveType } from './PrimitiveType'; import { getClosestPointOnLine } from '../../base/utilities/extensions/rayExtensions'; -import { type MeasureBoxDomainObject } from './MeasureBoxDomainObject'; +import { type BoxDomainObject } from './BoxDomainObject'; import { BaseDragger } from '../../base/domainObjectsHelpers/BaseDragger'; import { type VisualDomainObject, @@ -24,12 +24,12 @@ import { Vector3Pool } from '@cognite/reveal'; * It provides methods for scaling, translating, and rotating the box based on user interactions. * All geometry in this class assume Z-axis is up */ -export class MeasureBoxDragger extends BaseDragger { +export class BoxDragger extends BaseDragger { // ================================================== // INSTANCE FIELDS // ================================================== - private readonly _domainObject: MeasureBoxDomainObject; + private readonly _domainObject: BoxDomainObject; private readonly _face; private readonly _focusType: FocusType; @@ -59,7 +59,7 @@ export class MeasureBoxDragger extends BaseDragger { // CONSTRUCTOR // ================================================== - public constructor(props: CreateDraggerProps, domainObject: MeasureBoxDomainObject) { + public constructor(props: CreateDraggerProps, domainObject: BoxDomainObject) { super(props); const pickInfo = props.intersection.userData as BoxPickInfo; @@ -156,7 +156,7 @@ export class MeasureBoxDragger extends BaseDragger { const index = this._face.index; let deltaCenter: number; - if (this._domainObject.measureType !== MeasureType.Volume) { + if (this._domainObject.primitiveType !== PrimitiveType.Box) { deltaCenter = this._face.sign * deltaSize; } else { // Set new size diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts b/react-components/src/architecture/concrete/box/BoxOrLineEditTool.ts similarity index 56% rename from react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts rename to react-components/src/architecture/concrete/box/BoxOrLineEditTool.ts index bf9473d9d96..30f5983e233 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasurementTool.ts +++ b/react-components/src/architecture/concrete/box/BoxOrLineEditTool.ts @@ -2,80 +2,45 @@ * Copyright 2024 Cognite AS */ -import { MeasureBoxDomainObject } from './MeasureBoxDomainObject'; -import { type AnyIntersection, CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal'; -import { type BaseCommand } from '../../base/commands/BaseCommand'; +import { CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal'; import { isDomainObjectIntersection } from '../../base/domainObjectsHelpers/DomainObjectIntersection'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; import { type BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; import { type Vector3 } from 'three'; -import { MeasureBoxCreator } from './MeasureBoxCreator'; -import { MeasureType } from './MeasureType'; +import { PrimitiveType } from './PrimitiveType'; import { type BaseCreator } from '../../base/domainObjectsHelpers/BaseCreator'; -import { MeasureLineCreator } from './MeasureLineCreator'; import { BaseEditTool } from '../../base/commands/BaseEditTool'; -import { MeasureLineDomainObject } from './MeasureLineDomainObject'; import { type DomainObject } from '../../base/domainObjects/DomainObject'; -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 { ToggleMetricUnitsCommand } from '../../base/concreteCommands/ToggleMetricUnitsCommand'; -import { MeasureBoxRenderStyle } from './MeasureBoxRenderStyle'; -import { type VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; +import { BoxDomainObject } from './BoxDomainObject'; +import { LineDomainObject } from './LineDomainObject'; +import { TextRenderStyle } from './TextRenderStyle'; -export class MeasurementTool extends BaseEditTool { +export abstract class BoxOrLineEditTool extends BaseEditTool { // ================================================== // INSTANCE FIELDS // ================================================== private _creator: BaseCreator | undefined = undefined; - public measureType: MeasureType = MeasureType.None; // Default none, let the user decide + public primitiveType: PrimitiveType; + public defaultPrimitiveType: PrimitiveType; // ================================================== - // OVERRIDES of BaseCommand + // CONSTRUCTOR // ================================================== - public override get icon(): string { - return 'Ruler'; - } - - public override get tooltip(): TranslateKey { - return { key: 'MEASUREMENTS', fallback: 'Measurements' }; - } - - public override getToolbar(): Array { - return [ - 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 ToggleMetricUnitsCommand(), - new ShowMeasurementsOnTopCommand() - ]; - } - - public override getToolbarStyle(): PopupStyle { - return new PopupStyle({ bottom: 0, left: 0 }); + protected constructor(primitiveType: PrimitiveType = PrimitiveType.Box) { + super(); + this.defaultPrimitiveType = primitiveType; + this.primitiveType = this.defaultPrimitiveType; } // ================================================== // OVERRIDES of BaseTool // ================================================== - public override onActivate(): void { - super.onActivate(); - this.setAllMeasurementsVisible(true); - } - public override onDeactivate(): void { this.handleEscape(); - this.setAllMeasurementsVisible(false); super.onDeactivate(); } @@ -101,7 +66,7 @@ export class MeasurementTool extends BaseEditTool { public override async onHover(event: PointerEvent): Promise { // Handle when creator is set first - if (this.measureType !== MeasureType.None && this._creator !== undefined) { + if (this.primitiveType !== PrimitiveType.None && this._creator !== undefined) { const { _creator: creator } = this; if (!creator.preferIntersection) { // Hover in the "air" @@ -124,7 +89,7 @@ export class MeasurementTool extends BaseEditTool { this.renderTarget.setNavigateCursor(); return; } - if (this.getMeasurement(intersection) !== undefined) { + if (this.getIntersectedSelectableDomainObject(intersection) !== undefined) { this.renderTarget.setNavigateCursor(); return; } @@ -137,10 +102,10 @@ export class MeasurementTool extends BaseEditTool { return; } const intersection = await this.getIntersection(event); - const domainObject = this.getMeasurement(intersection); + const domainObject = this.getIntersectedSelectableDomainObject(intersection); if (!isDomainObjectIntersection(intersection) || domainObject === undefined) { this.defocusAll(); - if (this.measureType === MeasureType.None || intersection === undefined) { + if (this.primitiveType === PrimitiveType.None || intersection === undefined) { this.renderTarget.setNavigateCursor(); } else { this.setDefaultCursor(); @@ -148,11 +113,11 @@ export class MeasurementTool extends BaseEditTool { return; } // Set focus on the hovered object - if (domainObject instanceof MeasureLineDomainObject) { + if (domainObject instanceof LineDomainObject) { this.defocusAll(domainObject); domainObject.setFocusInteractive(FocusType.Focus); this.renderTarget.setDefaultCursor(); - } else if (domainObject instanceof MeasureBoxDomainObject) { + } else if (domainObject instanceof BoxDomainObject) { const pickInfo = intersection.userData as BoxPickInfo; if (pickInfo === undefined) { this.defocusAll(); @@ -175,7 +140,7 @@ export class MeasurementTool extends BaseEditTool { if (creator.addPoint(ray, undefined)) { if (creator.isFinished) { this._creator = undefined; - this.measureType = MeasureType.None; + this.primitiveType = this.defaultPrimitiveType; CommandsUpdater.update(renderTarget); } return; @@ -186,21 +151,21 @@ export class MeasurementTool extends BaseEditTool { // Click in the "air" return; } - const measurement = this.getMeasurement(intersection); - if (measurement !== undefined) { - this.deselectAll(measurement); - measurement.setSelectedInteractive(true); + const domainObject = this.getIntersectedSelectableDomainObject(intersection); + if (domainObject !== undefined) { + this.deselectAll(domainObject); + domainObject.setSelectedInteractive(true); return; } const ray = this.getRay(event); if (creator === undefined) { - const creator = (this._creator = createCreator(this.measureType)); + const creator = (this._creator = this.createCreator(this.primitiveType)); if (creator === undefined) { return; } if (creator.addPoint(ray, intersection)) { const { domainObject } = creator; - initializeStyle(domainObject, this.rootDomainObject); + this.initializeStyle(domainObject); this.deselectAll(); rootDomainObject.addChildInteractive(domainObject); domainObject.setSelectedInteractive(true); @@ -209,7 +174,7 @@ export class MeasurementTool extends BaseEditTool { } else { if (creator.addPoint(ray, intersection)) { if (creator.isFinished) { - this.measureType = MeasureType.None; + this.primitiveType = this.defaultPrimitiveType; CommandsUpdater.update(renderTarget); this._creator = undefined; } @@ -225,16 +190,12 @@ export class MeasurementTool extends BaseEditTool { } // ================================================== - // OVERRIDES of BaseEditTool + // VIRTUAL METHODS // ================================================== - protected override canBeSelected(domainObject: DomainObject): boolean { - return ( - domainObject instanceof MeasureBoxDomainObject || - domainObject instanceof MeasureLineDomainObject - ); + protected createCreator(_primitiveType: PrimitiveType): BaseCreator | undefined { + return undefined; } - // ================================================== // INSTANCE METHODS // ================================================== @@ -244,38 +205,14 @@ export class MeasurementTool extends BaseEditTool { return; } if (this._creator.handleEscape()) { - // Successfully created, set it back to none - this.measureType = MeasureType.None; + // Successfully created, set it back to default + this.primitiveType = this.defaultPrimitiveType; CommandsUpdater.update(this.renderTarget); } this._creator = undefined; } - private setAllMeasurementsVisible(visible: boolean): void { - for (const domainObject of this.rootDomainObject.getDescendants()) { - if (this.canBeSelected(domainObject)) { - domainObject.setVisibleInteractive(visible, this.renderTarget); - } - } - } - - private getMeasurement( - intersection: AnyIntersection | undefined - ): VisualDomainObject | undefined { - if (!isDomainObjectIntersection(intersection)) { - return undefined; - } - if (!this.canBeSelected(intersection.domainObject)) { - return undefined; - } - return intersection.domainObject as VisualDomainObject; - } - - private setCursor( - boxDomainObject: MeasureBoxDomainObject, - point: Vector3, - pickInfo: BoxPickInfo - ): void { + private setCursor(boxDomainObject: BoxDomainObject, point: Vector3, pickInfo: BoxPickInfo): void { if (pickInfo.focusType === FocusType.Body) { this.renderTarget.setMoveCursor(); } else if (pickInfo.focusType === FocusType.Face) { @@ -310,55 +247,35 @@ export class MeasurementTool extends BaseEditTool { } protected defocusAll(except?: DomainObject | undefined): void { - for (const domainObject of this.rootDomainObject.getDescendants()) { + for (const domainObject of this.getSelectable()) { if (except !== undefined && domainObject === except) { continue; } - if (domainObject instanceof MeasureLineDomainObject) { + if (domainObject instanceof LineDomainObject) { domainObject.setFocusInteractive(FocusType.None); } - if (domainObject instanceof MeasureBoxDomainObject) { + if (domainObject instanceof BoxDomainObject) { domainObject.setFocusInteractive(FocusType.None); } } } -} - -// ================================================== -// PRIVATE FUNCTIONS -// ================================================== - -function initializeStyle(domainObject: DomainObject, rootDomainObject: RootDomainObject): void { - // Just copy the style the depthTest field from any other measure DomainObject - const style = domainObject.getRenderStyle(); - if (!(style instanceof MeasureBoxRenderStyle)) { - return; - } - const otherBoxDomainObject = rootDomainObject.getDescendantByType(MeasureBoxDomainObject); - if (otherBoxDomainObject !== undefined) { - const otherStyle = otherBoxDomainObject.renderStyle; - style.depthTest = otherStyle.depthTest; - return; - } - const otherLineDomainObject = rootDomainObject.getDescendantByType(MeasureLineDomainObject); - if (otherLineDomainObject !== undefined) { - const otherStyle = otherLineDomainObject.renderStyle; - style.depthTest = otherStyle.depthTest; - } -} -function createCreator(measureType: MeasureType): BaseCreator | undefined { - switch (measureType) { - case MeasureType.Line: - case MeasureType.Polyline: - case MeasureType.Polygon: - return new MeasureLineCreator(measureType); + // ================================================== + // PRIVATE FUNCTIONS + // ================================================== - case MeasureType.HorizontalArea: - case MeasureType.VerticalArea: - case MeasureType.Volume: - return new MeasureBoxCreator(measureType); - default: - return undefined; + initializeStyle(domainObject: DomainObject): void { + // Just copy the style the depthTest field from any other measure DomainObject + const style = domainObject.getRenderStyle(); + if (!(style instanceof TextRenderStyle)) { + return; + } + for (const otherDomainObject of this.getSelectable()) { + const otherStyle = otherDomainObject.getRenderStyle(); + if (otherStyle instanceof TextRenderStyle) { + style.depthTest = otherStyle.depthTest; + return; + } + } } } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxRenderStyle.ts b/react-components/src/architecture/concrete/box/BoxRenderStyle.ts similarity index 83% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureBoxRenderStyle.ts rename to react-components/src/architecture/concrete/box/BoxRenderStyle.ts index a9d06c0f55b..dedf113168b 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxRenderStyle.ts +++ b/react-components/src/architecture/concrete/box/BoxRenderStyle.ts @@ -6,7 +6,7 @@ import { cloneDeep } from 'lodash'; import { TextRenderStyle } from './TextRenderStyle'; import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; -export class MeasureBoxRenderStyle extends TextRenderStyle { +export class BoxRenderStyle extends TextRenderStyle { // ================================================== // INSTANCE FIELDS // ================================================== @@ -19,6 +19,6 @@ export class MeasureBoxRenderStyle extends TextRenderStyle { // ================================================== public override clone(): RenderStyle { - return cloneDeep(this); + return cloneDeep(this); } } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxView.ts b/react-components/src/architecture/concrete/box/BoxView.ts similarity index 95% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureBoxView.ts rename to react-components/src/architecture/concrete/box/BoxView.ts index 33c18d94d9b..a5828be7b51 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxView.ts +++ b/react-components/src/architecture/concrete/box/BoxView.ts @@ -21,10 +21,10 @@ import { FrontSide, type PerspectiveCamera } from 'three'; -import { type MeasureBoxDomainObject, isValidSize } from './MeasureBoxDomainObject'; +import { type BoxDomainObject, isValidSize } from './BoxDomainObject'; import { type DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; -import { type MeasureBoxRenderStyle } from './MeasureBoxRenderStyle'; +import { type BoxRenderStyle } from './BoxRenderStyle'; import { GroupThreeView } from '../../base/views/GroupThreeView'; import { CDF_TO_VIEWER_TRANSFORMATION, @@ -44,7 +44,7 @@ import { import { BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; import { radToDeg } from 'three/src/math/MathUtils.js'; import { Range1 } from '../../base/utilities/geometry/Range1'; -import { MeasureType } from './MeasureType'; +import { PrimitiveType } from './PrimitiveType'; import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; const RELATIVE_RESIZE_RADIUS = 0.15; @@ -54,7 +54,7 @@ const TOP_FACE = new BoxFace(2); const CIRCULAR_SEGMENTS = 32; const RENDER_ORDER = 100; -export class MeasureBoxView extends GroupThreeView { +export class BoxView extends GroupThreeView { // ================================================== // INSTANCE FIELDS // ================================================== @@ -66,12 +66,12 @@ export class MeasureBoxView extends GroupThreeView { // INSTANCE PROPERTIES // ================================================== - public override get domainObject(): MeasureBoxDomainObject { - return super.domainObject as MeasureBoxDomainObject; + public override get domainObject(): BoxDomainObject { + return super.domainObject as BoxDomainObject; } - protected override get style(): MeasureBoxRenderStyle { - return super.style as MeasureBoxRenderStyle; + protected override get style(): BoxRenderStyle { + return super.style as BoxRenderStyle; } // ================================================== @@ -525,11 +525,11 @@ export class MeasureBoxView extends GroupThreeView { private isFaceVisible(boxFace: BoxFace): boolean { const { domainObject } = this; - switch (domainObject.measureType) { - case MeasureType.VerticalArea: + switch (domainObject.primitiveType) { + case PrimitiveType.VerticalArea: return boxFace.index === 1; // Y Face visible - case MeasureType.HorizontalArea: + case PrimitiveType.HorizontalArea: return boxFace.index === 2; // Z face visible } return true; @@ -567,8 +567,8 @@ function showMarkers(focusType: FocusType): boolean { function updateSolidMaterial( material: MeshPhongMaterial, - domainObject: MeasureBoxDomainObject, - style: MeasureBoxRenderStyle + domainObject: BoxDomainObject, + style: BoxRenderStyle ): void { const color = domainObject.getColorByColorType(style.colorType); const isSelected = domainObject.isSelected; @@ -589,8 +589,8 @@ function updateSolidMaterial( function updateLineSegmentsMaterial( material: LineBasicMaterial, - domainObject: MeasureBoxDomainObject, - style: MeasureBoxRenderStyle + domainObject: BoxDomainObject, + style: BoxRenderStyle ): void { const color = domainObject.getColorByColorType(style.colorType); material.color = color; @@ -601,8 +601,8 @@ function updateLineSegmentsMaterial( function updateMarkerMaterial( material: MeshPhongMaterial, - domainObject: MeasureBoxDomainObject, - style: MeasureBoxRenderStyle, + domainObject: BoxDomainObject, + style: BoxRenderStyle, hasFocus: boolean ): void { material.color = ARROW_AND_RING_COLOR; @@ -622,11 +622,7 @@ function updateMarkerMaterial( // PRIVATE FUNCTIONS: Create object3D's // ================================================== -function createSprite( - text: string, - style: MeasureBoxRenderStyle, - height: number -): Sprite | undefined { +function createSprite(text: string, style: BoxRenderStyle, height: number): Sprite | undefined { const result = createSpriteWithText(text, height, style.textColor, style.textBgColor); if (result === undefined) { return undefined; @@ -638,12 +634,8 @@ function createSprite( return result; } -function adjustLabel( - point: Vector3, - domainObject: MeasureBoxDomainObject, - spriteHeight: number -): void { - if (domainObject.measureType !== MeasureType.VerticalArea) { +function adjustLabel(point: Vector3, domainObject: BoxDomainObject, spriteHeight: number): void { + if (domainObject.primitiveType !== PrimitiveType.VerticalArea) { point.y += (1.1 * spriteHeight) / 2; } } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineCreator.ts b/react-components/src/architecture/concrete/box/LineCreator.ts similarity index 81% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureLineCreator.ts rename to react-components/src/architecture/concrete/box/LineCreator.ts index 5c00b7669bc..5b4ded3b467 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineCreator.ts +++ b/react-components/src/architecture/concrete/box/LineCreator.ts @@ -3,31 +3,31 @@ */ import { Plane, type Ray, Vector3 } from 'three'; -import { MeasureType } from './MeasureType'; +import { PrimitiveType } from './PrimitiveType'; import { BaseCreator } from '../../base/domainObjectsHelpers/BaseCreator'; -import { MeasureLineDomainObject } from './MeasureLineDomainObject'; import { copy } from '../../base/utilities/extensions/arrayExtensions'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; import { type DomainObject } from '../../base/domainObjects/DomainObject'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; +import { type LineDomainObject } from './LineDomainObject'; /** - * Helper class for generate a MeasureLineDomainObject by clicking around + * Helper class for generate a LineDomainObject by clicking around */ -export class MeasureLineCreator extends BaseCreator { +export class LineCreator extends BaseCreator { // ================================================== // INSTANCE FIELDS // ================================================== - private readonly _domainObject: MeasureLineDomainObject; + private readonly _domainObject: LineDomainObject; // ================================================== // CONSTRUCTOR // ================================================== - constructor(measureType: MeasureType) { + constructor(domainObject: LineDomainObject) { super(); - this._domainObject = new MeasureLineDomainObject(measureType); + this._domainObject = domainObject; this._domainObject.focusType = FocusType.Pending; } @@ -48,14 +48,14 @@ export class MeasureLineCreator extends BaseCreator { } public override get maximumPointCount(): number { - switch (this._domainObject.measureType) { - case MeasureType.Line: + switch (this._domainObject.primitiveType) { + case PrimitiveType.Line: return 2; - case MeasureType.Polyline: - case MeasureType.Polygon: + case PrimitiveType.Polyline: + case PrimitiveType.Polygon: return Number.MAX_SAFE_INTEGER; default: - throw new Error('Unknown measurement type'); + throw new Error('Unknown primitiveType'); } } diff --git a/react-components/src/architecture/concrete/box/LineDomainObject.ts b/react-components/src/architecture/concrete/box/LineDomainObject.ts index bc2714c6c7e..22798c1fc42 100644 --- a/react-components/src/architecture/concrete/box/LineDomainObject.ts +++ b/react-components/src/architecture/concrete/box/LineDomainObject.ts @@ -4,10 +4,10 @@ import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; import { type ThreeView } from '../../base/views/ThreeView'; -import { MeasureLineView } from './MeasureLineView'; -import { Color, Vector3 } from 'three'; -import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; -import { MeasureLineRenderStyle } from './MeasureLineRenderStyle'; +import { LineView } from './LineView'; +import { Vector3 } from 'three'; +import { PrimitiveType } from './PrimitiveType'; +import { LineRenderStyle } from './LineRenderStyle'; import { getHorizontalCrossProduct, horizontalDistanceTo, @@ -20,51 +20,42 @@ import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; import { VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; -export class LineDomainObject extends VisualDomainObject { +export abstract class LineDomainObject extends VisualDomainObject { // ================================================== // INSTANCE FIELDS // ================================================== public readonly points: Vector3[] = []; - private readonly _measureType: MeasureType; + private readonly _primitiveType: PrimitiveType; public focusType = FocusType.None; // ================================================== // INSTANCE PROPERTIES // ================================================== - public get renderStyle(): MeasureLineRenderStyle { - return this.getRenderStyle() as MeasureLineRenderStyle; + public get renderStyle(): LineRenderStyle { + return this.getRenderStyle() as LineRenderStyle; } - public get measureType(): MeasureType { - return this._measureType; + public get primitiveType(): PrimitiveType { + return this._primitiveType; } // ================================================== // CONSTRUCTOR // ================================================== - public constructor(measureType: MeasureType) { + protected constructor(primitiveType: PrimitiveType) { super(); - this._measureType = measureType; - this.color = new Color(Color.NAMES.red); + this._primitiveType = primitiveType; } // ================================================== // OVERRIDES of DomainObject // ================================================== - public override get icon(): string { - return getIconByMeasureType(this.measureType); - } - - public override get typeName(): string { - return getNameByMeasureType(this.measureType); - } - public override createRenderStyle(): RenderStyle | undefined { - return new MeasureLineRenderStyle(); + return new LineRenderStyle(); } public override get hasPanelInfo(): boolean { @@ -81,19 +72,19 @@ export class LineDomainObject extends VisualDomainObject { return undefined; } const info = new PanelInfo(); - switch (this.measureType) { - case MeasureType.Line: + switch (this.primitiveType) { + case PrimitiveType.Line: info.setHeader('MEASUREMENTS_LINE', 'Line'); add('MEASUREMENTS_LENGTH', 'Length', this.getTotalLength()); add('MEASUREMENTS_HORIZONTAL_LENGTH', 'Horizontal length', this.getHorizontalLength()); add('MEASUREMENTS_VERTICAL_LENGTH', 'Vertical length', this.getVerticalLength()); break; - case MeasureType.Polyline: + case PrimitiveType.Polyline: info.setHeader('MEASUREMENTS_POLYLINE', 'Polyline'); add('MEASUREMENTS_TOTAL_LENGTH', 'Total length', this.getTotalLength()); break; - case MeasureType.Polygon: + case PrimitiveType.Polygon: info.setHeader('MEASUREMENTS_POLYGON', 'Polygon'); add('MEASUREMENTS_TOTAL_LENGTH', 'Total length', this.getTotalLength()); if (this.points.length > 2) { @@ -107,7 +98,7 @@ export class LineDomainObject extends VisualDomainObject { break; default: - throw new Error('Unknown MeasureType type'); + throw new Error('Unknown PrimitiveType type'); } return info; @@ -121,7 +112,7 @@ export class LineDomainObject extends VisualDomainObject { // ================================================== protected override createThreeView(): ThreeView | undefined { - return new MeasureLineView(); + return new LineView(); } // ================================================== @@ -187,7 +178,7 @@ export class LineDomainObject extends VisualDomainObject { for (let index = 1; index <= count; index++) { p1.copy(points[index % count]); - p1.sub(first); // Translate down to first point, to increase acceracy + p1.sub(first); // Translate down to first point, to increase accuracy sum += getHorizontalCrossProduct(p0, p1); p0.copy(p1); } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineRenderStyle.ts b/react-components/src/architecture/concrete/box/LineRenderStyle.ts similarity index 85% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureLineRenderStyle.ts rename to react-components/src/architecture/concrete/box/LineRenderStyle.ts index fa01d5c316d..45843cf0265 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineRenderStyle.ts +++ b/react-components/src/architecture/concrete/box/LineRenderStyle.ts @@ -6,7 +6,7 @@ import { cloneDeep } from 'lodash'; import { TextRenderStyle } from './TextRenderStyle'; import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; -export class MeasureLineRenderStyle extends TextRenderStyle { +export class LineRenderStyle extends TextRenderStyle { // ================================================== // INSTANCE FIELDS // ================================================== @@ -21,6 +21,6 @@ export class MeasureLineRenderStyle extends TextRenderStyle { // ================================================== public override clone(): RenderStyle { - return cloneDeep(this); + return cloneDeep(this); } } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineView.ts b/react-components/src/architecture/concrete/box/LineView.ts similarity index 88% rename from react-components/src/architecture/concrete/boxDomainObject/MeasureLineView.ts rename to react-components/src/architecture/concrete/box/LineView.ts index a78445f150c..8972bb71711 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineView.ts +++ b/react-components/src/architecture/concrete/box/LineView.ts @@ -18,10 +18,10 @@ import { Vector3 } from 'three'; import { Wireframe } from 'three/examples/jsm/lines/Wireframe.js'; -import { MeasureLineDomainObject } from './MeasureLineDomainObject'; +import { LineDomainObject } from './LineDomainObject'; import { DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; import { Changes } from '../../base/domainObjectsHelpers/Changes'; -import { MeasureLineRenderStyle } from './MeasureLineRenderStyle'; +import { LineRenderStyle } from './LineRenderStyle'; import { GroupThreeView } from '../../base/views/GroupThreeView'; import { CDF_TO_VIEWER_TRANSFORMATION, @@ -30,7 +30,7 @@ import { } from '@cognite/reveal'; import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js'; import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'; -import { MeasureType } from './MeasureType'; +import { PrimitiveType } from './PrimitiveType'; import { createSpriteWithText } from '../../base/utilities/sprites/createSprite'; import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'; import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; @@ -42,17 +42,17 @@ import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; const CYLINDER_DEFAULT_AXIS = new Vector3(0, 1, 0); const RENDER_ORDER = 100; -export class MeasureLineView extends GroupThreeView { +export class LineView extends GroupThreeView { // ================================================== // INSTANCE PROPERTIES // ================================================== - public override get domainObject(): MeasureLineDomainObject { - return super.domainObject as MeasureLineDomainObject; + public override get domainObject(): LineDomainObject { + return super.domainObject as LineDomainObject; } - protected override get style(): MeasureLineRenderStyle { - return super.style as MeasureLineRenderStyle; + protected override get style(): LineRenderStyle { + return super.style as LineRenderStyle; } // ================================================== @@ -120,7 +120,7 @@ export class MeasureLineView extends GroupThreeView { if (closestDistance !== undefined) { closestFinder.minDistance = closestDistance; } - const loopLength = domainObject.measureType === MeasureType.Polygon ? length + 1 : length; + const loopLength = domainObject.primitiveType === PrimitiveType.Polygon ? length + 1 : length; for (let i = 0; i < loopLength; i++) { thisPoint.copy(points[i % length]); thisPoint.applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION); @@ -163,7 +163,7 @@ export class MeasureLineView extends GroupThreeView { return undefined; } const geometries: CylinderGeometry[] = []; - const loopLength = domainObject.measureType === MeasureType.Polygon ? length + 1 : length; + const loopLength = domainObject.primitiveType === PrimitiveType.Polygon ? length + 1 : length; // Just allocate all needed objects once const prevPoint = new Vector3(); @@ -262,7 +262,7 @@ export class MeasureLineView extends GroupThreeView { if (spriteHeight <= 0) { return; } - const loopLength = domainObject.measureType === MeasureType.Polygon ? length : length - 1; + const loopLength = domainObject.primitiveType === PrimitiveType.Polygon ? length : length - 1; const center = new Vector3(); for (let i = 0; i < loopLength; i++) { const point1 = points[i % length]; @@ -292,14 +292,14 @@ export class MeasureLineView extends GroupThreeView { // PRIVATE FUNCTIONS: Create object3D's // ================================================== -function createVertices(domainObject: MeasureLineDomainObject): number[] | undefined { +function createVertices(domainObject: LineDomainObject): number[] | undefined { const { points } = domainObject; const { length } = points; if (length < 2) { return undefined; } const vertices: number[] = []; - const loopLength = domainObject.measureType === MeasureType.Polygon ? length + 1 : length; + const loopLength = domainObject.primitiveType === PrimitiveType.Polygon ? length + 1 : length; for (let i = 0; i < loopLength; i++) { const point = points[i % length].clone(); @@ -312,11 +312,7 @@ function createVertices(domainObject: MeasureLineDomainObject): number[] | undef return vertices; } -function createSprite( - text: string, - style: MeasureLineRenderStyle, - height: number -): Sprite | undefined { +function createSprite(text: string, style: LineRenderStyle, height: number): Sprite | undefined { const result = createSpriteWithText(text, height, style.textColor, style.textBgColor); if (result === undefined) { return undefined; @@ -330,8 +326,8 @@ function createSprite( function updateSolidMaterial( material: MeshPhongMaterial, - boxDomainObject: MeasureLineDomainObject, - style: MeasureLineRenderStyle + boxDomainObject: LineDomainObject, + style: LineRenderStyle ): void { const color = boxDomainObject.getColorByColorType(style.colorType); material.color = color; @@ -347,15 +343,15 @@ function updateSolidMaterial( function adjustLabel( point: Vector3, - domainObject: MeasureLineDomainObject, - style: MeasureLineRenderStyle, + domainObject: LineDomainObject, + style: LineRenderStyle, spriteHeight: number ): void { - if (domainObject.measureType !== MeasureType.VerticalArea) { + if (domainObject.primitiveType !== PrimitiveType.VerticalArea) { point.y += (1.1 * spriteHeight) / 2 + style.pipeRadius; } } -function getRadius(domainObject: MeasureLineDomainObject, style: MeasureLineRenderStyle): number { +function getRadius(domainObject: LineDomainObject, style: LineRenderStyle): number { return domainObject.isSelected ? style.selectedPipeRadius : style.pipeRadius; } diff --git a/react-components/src/architecture/concrete/box/PrimitiveType.ts b/react-components/src/architecture/concrete/box/PrimitiveType.ts new file mode 100644 index 00000000000..88186de0706 --- /dev/null +++ b/react-components/src/architecture/concrete/box/PrimitiveType.ts @@ -0,0 +1,13 @@ +/*! + * Copyright 2024 Cognite AS + */ + +export enum PrimitiveType { + None, + Line, + Polyline, + Polygon, + HorizontalArea, + VerticalArea, + Box +} diff --git a/react-components/src/architecture/concrete/boxDomainObject/TextRenderStyle.ts b/react-components/src/architecture/concrete/box/TextRenderStyle.ts similarity index 90% rename from react-components/src/architecture/concrete/boxDomainObject/TextRenderStyle.ts rename to react-components/src/architecture/concrete/box/TextRenderStyle.ts index ebf007ce575..668af907ff4 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/TextRenderStyle.ts +++ b/react-components/src/architecture/concrete/box/TextRenderStyle.ts @@ -17,5 +17,5 @@ export abstract class TextRenderStyle 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 measurement object for box and average of lenght of line segments for line + public relativeTextSize = 0.05; // Relative to diagonal of the object for box and average of length of line segments for line } diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts deleted file mode 100644 index 14d43827366..00000000000 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureBoxDomainObject.ts +++ /dev/null @@ -1,314 +0,0 @@ -/*! - * Copyright 2024 Cognite AS - */ - -import { MeasureBoxRenderStyle } from './MeasureBoxRenderStyle'; -import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; -import { type ThreeView } from '../../base/views/ThreeView'; -import { MeasureBoxView } from './MeasureBoxView'; -import { Box3, Color, Matrix4, Vector3 } from 'three'; -import { Changes } from '../../base/domainObjectsHelpers/Changes'; -import { BoxFace } from '../../base/utilities/box/BoxFace'; -import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; -import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; -import { type BoxPickInfo } from '../../base/utilities/box/BoxPickInfo'; -import { type BaseDragger } from '../../base/domainObjectsHelpers/BaseDragger'; -import { MeasureBoxDragger } from './MeasureBoxDragger'; -import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; -import { radToDeg } from 'three/src/math/MathUtils.js'; -import { - VisualDomainObject, - type CreateDraggerProps -} from '../../base/domainObjects/VisualDomainObject'; -import { Range3 } from '../../base/utilities/geometry/Range3'; -import { CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal'; -import { type DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; -import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; -import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; - -export const MIN_BOX_SIZE = 0.01; - -export class MeasureBoxDomainObject extends VisualDomainObject { - // ================================================== - // INSTANCE FIELDS - // ================================================== - - public readonly size = new Vector3().setScalar(MIN_BOX_SIZE); - public readonly center = new Vector3(); - public zRotation = 0; // Angle in radians in interval [0, 2*Pi> - - // For focus when edit in 3D (Used when isSelected is true only) - public focusType: FocusType = FocusType.None; - public focusFace: BoxFace | undefined = undefined; - private readonly _measureType: MeasureType; - - // ================================================== - // INSTANCE PROPERTIES - // ================================================== - - public get renderStyle(): MeasureBoxRenderStyle { - return this.getRenderStyle() as MeasureBoxRenderStyle; - } - - public get measureType(): MeasureType { - return this._measureType; - } - - // ================================================== - // CONSTRUCTOR - // ================================================== - - public constructor(measureType: MeasureType) { - super(); - this._measureType = measureType; - this.color = new Color(Color.NAMES.magenta); - } - - // ================================================== - // OVERRIDES of DomainObject - // ================================================== - - public override get icon(): string { - return getIconByMeasureType(this.measureType); - } - - public override get typeName(): string { - return getNameByMeasureType(this.measureType); - } - - public override createRenderStyle(): RenderStyle | undefined { - return new MeasureBoxRenderStyle(); - } - - public override createDragger(props: CreateDraggerProps): BaseDragger | undefined { - const pickInfo = props.intersection.userData as BoxPickInfo; - if (pickInfo === undefined) { - return undefined; // If the BoxPickInfo isn't specified, no dragger is created - } - return new MeasureBoxDragger(props, this); - } - - protected override notifyCore(change: DomainObjectChange): void { - super.notifyCore(change); - - // Experimental code for crop box (let it stay here) - // if (this.isUseAsCropBox) { - // if (change.isChanged(Changes.deleted)) { - // this.setUseAsCropBox(false); - // } - // if (change.isChanged(Changes.geometry)) { - // this.setUseAsCropBox(true); - // } - // } else if (change.isChanged(Changes.selected) && this.isSelected) { - // const root = this.root as RootDomainObject; - // if (root instanceof RootDomainObject && root.renderTarget.isGlobalCropBoxActive) { - // this.setUseAsCropBox(true); - // } - // } - } - - public override get hasPanelInfo(): boolean { - return true; - } - - public override getPanelInfoStyle(): PopupStyle { - // bottom = 66 because the toolbar is below - return new PopupStyle({ bottom: 66, left: 0 }); - } - - public override getPanelInfo(): PanelInfo | undefined { - const info = new PanelInfo(); - const { measureType } = this; - const isFinished = this.focusType !== FocusType.Pending; - - switch (measureType) { - case MeasureType.HorizontalArea: - info.setHeader('MEASUREMENTS_HORIZONTAL_AREA', 'Horizontal area'); - break; - case MeasureType.VerticalArea: - info.setHeader('MEASUREMENTS_VERTICAL_AREA', 'Vertical area'); - break; - case MeasureType.Volume: - info.setHeader('MEASUREMENTS_VOLUME', 'Volume'); - break; - } - if (isFinished || isValidSize(this.size.x)) { - add('MEASUREMENTS_LENGTH', 'Length', this.size.x, Quantity.Length); - } - if (measureType !== MeasureType.VerticalArea && (isFinished || isValidSize(this.size.y))) { - add('MEASUREMENTS_DEPTH', 'Depth', this.size.y, Quantity.Length); - } - if (measureType !== MeasureType.HorizontalArea && (isFinished || isValidSize(this.size.z))) { - add('MEASUREMENTS_HEIGHT', 'Height', this.size.z, Quantity.Length); - } - if (measureType !== MeasureType.Volume && (isFinished || this.hasArea)) { - add('MEASUREMENTS_AREA', 'Area', this.area, Quantity.Area); - } - if (measureType === MeasureType.Volume && (isFinished || this.hasHorizontalArea)) { - add('MEASUREMENTS_HORIZONTAL_AREA', 'Horizontal area', this.horizontalArea, Quantity.Area); - } - if (measureType === MeasureType.Volume && (isFinished || this.hasVolume)) { - add('MEASUREMENTS_VOLUME', 'Volume', this.volume, Quantity.Volume); - } - // I forgot to add text for rotation angle before the deadline, so I used a icon instead. - if (this.zRotation !== 0 && isFinished) { - info.add({ - key: '', - icon: 'Angle', - value: radToDeg(this.zRotation), - quantity: Quantity.Degrees - }); - } - return info; - - function add(key: string, fallback: string, value: number, quantity: Quantity): void { - info.add({ key, fallback, value, quantity }); - } - } - - // ================================================== - // OVERRIDES of VisualDomainObject - // ================================================== - - protected override createThreeView(): ThreeView | undefined { - return new MeasureBoxView(); - } - - // ================================================== - // INSTANCE METHODS: Getters/Properties - // ================================================== - - public get diagonal(): number { - return this.size.length(); - } - - public get hasArea(): boolean { - let count = 0; - if (isValidSize(this.size.x)) count++; - if (isValidSize(this.size.y)) count++; - if (isValidSize(this.size.z)) count++; - return count >= 2; - } - - public get area(): number { - switch (this.measureType) { - case MeasureType.HorizontalArea: - return this.size.x * this.size.y; - case MeasureType.VerticalArea: - return this.size.x * this.size.z; - case MeasureType.Volume: { - const a = this.size.x * this.size.y + this.size.y * this.size.z + this.size.z * this.size.x; - return a * 2; - } - default: - throw new Error('Unknown MeasureType type'); - } - } - - public get hasHorizontalArea(): boolean { - return isValidSize(this.size.x) && isValidSize(this.size.y); - } - - public get horizontalArea(): number { - return this.size.x * this.size.y; - } - - public get hasVolume(): boolean { - return isValidSize(this.size.x) && isValidSize(this.size.y) && isValidSize(this.size.z); - } - - public get volume(): number { - return this.size.x * this.size.y * this.size.z; - } - - public getBoundingBox(): Box3 { - const matrix = this.getMatrix(); - const boundingBox = new Box3().makeEmpty(); - const unitCube = Range3.createCube(0.5); - const corner = new Vector3(); - for (let i = 0; i < 8; i++) { - unitCube.getCornerPoint(i, corner); - corner.applyMatrix4(matrix); - boundingBox.expandByPoint(corner); - } - return boundingBox; - } - - public getRotationMatrix(matrix: Matrix4 = new Matrix4()): Matrix4 { - matrix.makeRotationZ(this.zRotation); - return matrix; - } - - public getMatrix(matrix: Matrix4 = new Matrix4()): Matrix4 { - return this.getScaledMatrix(this.size, matrix); - } - - public getScaledMatrix(scale: Vector3, matrix: Matrix4 = new Matrix4()): Matrix4 { - matrix = this.getRotationMatrix(matrix); - matrix.setPosition(this.center); - matrix.scale(scale); - return matrix; - } - - // ================================================== - // INSTANCE METHODS: Others - // ================================================== - - public forceMinSize(): void { - const { size } = this; - size.x = Math.max(MIN_BOX_SIZE, size.x); - size.y = Math.max(MIN_BOX_SIZE, size.y); - size.z = Math.max(MIN_BOX_SIZE, size.z); - } - - public setFocusInteractive(focusType: FocusType, focusFace?: BoxFace): boolean { - if (focusType === FocusType.None) { - if (this.focusType === FocusType.None) { - return false; // No change - } - this.focusType = FocusType.None; - this.focusFace = undefined; // Ignore input face - } else { - if (focusType === this.focusType && BoxFace.equals(this.focusFace, focusFace)) { - return false; // No change - } - this.focusType = focusType; - this.focusFace = focusFace; - } - this.notify(Changes.focus); - return true; - } - - public get isUseAsCropBox(): boolean { - const root = this.rootDomainObject; - if (root === undefined) { - return false; - } - return root.renderTarget.isGlobalCropBox(this); - } - - public setUseAsCropBox(use: boolean): void { - const root = this.rootDomainObject; - if (root === undefined) { - return; - } - if (!use) { - root.renderTarget.clearGlobalCropBox(); - } else { - const boundingBox = this.getBoundingBox(); - boundingBox.applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION); - const matrix = this.getMatrix(); - matrix.premultiply(CDF_TO_VIEWER_TRANSFORMATION); - const planes = BoxFace.createClippingPlanes(matrix); - root.renderTarget.setGlobalCropBox(planes, boundingBox, this); - } - } -} - -// ================================================== -// PUBLIC FUNCTIONS -// ================================================== - -export function isValidSize(value: number): boolean { - return value > MIN_BOX_SIZE; -} diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts deleted file mode 100644 index d4de03e7fe2..00000000000 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureLineDomainObject.ts +++ /dev/null @@ -1,205 +0,0 @@ -/*! - * Copyright 2024 Cognite AS - */ - -import { type RenderStyle } from '../../base/domainObjectsHelpers/RenderStyle'; -import { type ThreeView } from '../../base/views/ThreeView'; -import { MeasureLineView } from './MeasureLineView'; -import { Color, Vector3 } from 'three'; -import { MeasureType, getIconByMeasureType, getNameByMeasureType } from './MeasureType'; -import { MeasureLineRenderStyle } from './MeasureLineRenderStyle'; -import { - getHorizontalCrossProduct, - horizontalDistanceTo, - verticalDistanceTo -} from '../../base/utilities/extensions/vectorExtensions'; -import { PanelInfo } from '../../base/domainObjectsHelpers/PanelInfo'; -import { Changes } from '../../base/domainObjectsHelpers/Changes'; -import { FocusType } from '../../base/domainObjectsHelpers/FocusType'; -import { Quantity } from '../../base/domainObjectsHelpers/Quantity'; -import { PopupStyle } from '../../base/domainObjectsHelpers/PopupStyle'; -import { VisualDomainObject } from '../../base/domainObjects/VisualDomainObject'; - -export class MeasureLineDomainObject extends VisualDomainObject { - // ================================================== - // INSTANCE FIELDS - // ================================================== - - public readonly points: Vector3[] = []; - private readonly _measureType: MeasureType; - public focusType = FocusType.None; - - // ================================================== - // INSTANCE PROPERTIES - // ================================================== - - public get renderStyle(): MeasureLineRenderStyle { - return this.getRenderStyle() as MeasureLineRenderStyle; - } - - public get measureType(): MeasureType { - return this._measureType; - } - - // ================================================== - // CONSTRUCTOR - // ================================================== - - public constructor(measureType: MeasureType) { - super(); - this._measureType = measureType; - this.color = new Color(Color.NAMES.red); - } - - // ================================================== - // OVERRIDES of DomainObject - // ================================================== - - public override get icon(): string { - return getIconByMeasureType(this.measureType); - } - - public override get typeName(): string { - return getNameByMeasureType(this.measureType); - } - - public override createRenderStyle(): RenderStyle | undefined { - return new MeasureLineRenderStyle(); - } - - public override get hasPanelInfo(): boolean { - return true; - } - - public override getPanelInfoStyle(): PopupStyle { - // bottom = 66 because the toolbar is below - return new PopupStyle({ bottom: 66, left: 0 }); - } - - public override getPanelInfo(): PanelInfo | undefined { - if (this.focusType === FocusType.Pending && this.points.length <= 1) { - return undefined; - } - const info = new PanelInfo(); - switch (this.measureType) { - case MeasureType.Line: - info.setHeader('MEASUREMENTS_LINE', 'Line'); - add('MEASUREMENTS_LENGTH', 'Length', this.getTotalLength()); - add('MEASUREMENTS_HORIZONTAL_LENGTH', 'Horizontal length', this.getHorizontalLength()); - add('MEASUREMENTS_VERTICAL_LENGTH', 'Vertical length', this.getVerticalLength()); - break; - - case MeasureType.Polyline: - info.setHeader('MEASUREMENTS_POLYLINE', 'Polyline'); - add('MEASUREMENTS_TOTAL_LENGTH', 'Total length', this.getTotalLength()); - break; - case MeasureType.Polygon: - info.setHeader('MEASUREMENTS_POLYGON', 'Polygon'); - add('MEASUREMENTS_TOTAL_LENGTH', 'Total length', this.getTotalLength()); - if (this.points.length > 2) { - add( - 'MEASUREMENTS_HORIZONTAL_AREA', - 'Horizontal area', - this.getHorizontalArea(), - Quantity.Area - ); - } - break; - - default: - throw new Error('Unknown MeasureType type'); - } - return info; - - function add(key: string, fallback: string, value: number, quantity = Quantity.Length): void { - info.add({ key, fallback, value, quantity }); - } - } - - // ================================================== - // OVERRIDES of VisualDomainObject - // ================================================== - - protected override createThreeView(): ThreeView | undefined { - return new MeasureLineView(); - } - - // ================================================== - // INSTANCE METHODS - // ================================================== - - public getTotalLength(): number { - let prevPoint: Vector3 | undefined; - let sum = 0.0; - for (const point of this.points) { - if (prevPoint !== undefined) { - sum += point.distanceTo(prevPoint); - } - prevPoint = point; - } - return sum; - } - - public getAverageLength(): number { - const count = this.points.length; - if (count <= 1) { - return 0; - } - return this.getTotalLength() / (count - 1); - } - - public getHorizontalLength(): number { - let prevPoint: Vector3 | undefined; - let sum = 0.0; - for (const point of this.points) { - if (prevPoint !== undefined) { - sum += horizontalDistanceTo(point, prevPoint); - continue; - } - prevPoint = point; - } - return sum; - } - - public getVerticalLength(): number { - let prevPoint: Vector3 | undefined; - let sum = 0.0; - for (const point of this.points) { - if (prevPoint !== undefined) { - sum += verticalDistanceTo(point, prevPoint); - continue; - } - prevPoint = point; - } - return sum; - } - - public getHorizontalArea(): number { - const { points } = this; - const count = points.length; - if (count <= 2) { - return 0; - } - let sum = 0.0; - const first = points[0]; - const p0 = new Vector3(); - const p1 = new Vector3(); - - for (let index = 1; index <= count; index++) { - p1.copy(points[index % count]); - p1.sub(first); // Translate down to first point, to increase acceracy - sum += getHorizontalCrossProduct(p0, p1); - p0.copy(p1); - } - return Math.abs(sum) / 2; - } - - public setFocusInteractive(focusType: FocusType): boolean { - if (this.focusType === focusType) { - return false; - } - this.focusType = focusType; - this.notify(Changes.focus); - return true; - } -} diff --git a/react-components/src/architecture/concrete/boxDomainObject/MeasureType.ts b/react-components/src/architecture/concrete/boxDomainObject/MeasureType.ts deleted file mode 100644 index ef8b55de138..00000000000 --- a/react-components/src/architecture/concrete/boxDomainObject/MeasureType.ts +++ /dev/null @@ -1,92 +0,0 @@ -/*! - * Copyright 2024 Cognite AS - */ - -import { type TranslateKey } from '../../base/utilities/TranslateKey'; - -export enum MeasureType { - None, - Line, - Polyline, - Polygon, - HorizontalArea, - VerticalArea, - Volume -} - -export function getIconByMeasureType(measureType: MeasureType): string { - switch (measureType) { - case MeasureType.Line: - return 'VectorLine'; - case MeasureType.Polyline: - return 'VectorZigzag'; - case MeasureType.Polygon: - return 'Polygon'; - case MeasureType.HorizontalArea: - return 'FrameTool'; - case MeasureType.VerticalArea: - return 'Perspective'; - case MeasureType.Volume: - return 'Cube'; - default: - throw new Error('Unknown MeasureType type'); - } -} - -export function getNameByMeasureType(measureType: MeasureType): string { - switch (measureType) { - case MeasureType.Line: - return 'Line'; - case MeasureType.Polyline: - return 'Polyline'; - case MeasureType.Polygon: - return 'Polygon'; - case MeasureType.HorizontalArea: - return 'Horizontal area'; - case MeasureType.VerticalArea: - return 'Vertical area'; - case MeasureType.Volume: - return 'Volume'; - default: - throw new Error('Unknown MeasureType type'); - } -} - -export function getTooltipByMeasureType(measureType: MeasureType): TranslateKey { - switch (measureType) { - case MeasureType.Line: - return { - key: 'MEASUREMENTS_ADD_LINE', - fallback: 'Measure distance between two points. Click at the start point and the end point.' - }; - case MeasureType.Polyline: - return { - key: 'MEASUREMENTS_ADD_POLYLINE', - fallback: - 'Measure the length of a continuous polyline. Click at any number of points and end with Esc.' - }; - case MeasureType.Polygon: - return { - key: 'MEASUREMENTS_ADD_POLYGON', - fallback: 'Measure an area of a polygon. Click at least 3 points and end with Esc.' - }; - case MeasureType.VerticalArea: - return { - key: 'MEASUREMENTS_ADD_VERTICAL_AREA', - fallback: 'Measure rectangular vertical Area. Click at two points in a vertical plan.' - }; - case MeasureType.HorizontalArea: - return { - key: 'MEASUREMENTS_ADD_HORIZONTAL_AREA', - fallback: 'Measure rectangular horizontal Area. Click at three points in a horizontal plan.' - }; - case MeasureType.Volume: - return { - key: 'MEASUREMENTS_ADD_VOLUME', - fallback: - 'Measure volume of a box. Click at three points in a horizontal plan and the fourth to give it height.' - }; - default: - throw new Error('Unknown MeasureType type'); - } -} diff --git a/react-components/src/architecture/concrete/boxDomainObject/SetMeasurementTypeCommand.ts b/react-components/src/architecture/concrete/boxDomainObject/SetMeasurementTypeCommand.ts deleted file mode 100644 index be21b504d02..00000000000 --- a/react-components/src/architecture/concrete/boxDomainObject/SetMeasurementTypeCommand.ts +++ /dev/null @@ -1,80 +0,0 @@ -/*! - * Copyright 2024 Cognite AS - */ - -import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; -import { type BaseCommand } from '../../base/commands/BaseCommand'; -import { MeasureType, getIconByMeasureType, getTooltipByMeasureType } from './MeasureType'; -import { MeasurementTool } from './MeasurementTool'; -import { type TranslateKey } from '../../base/utilities/TranslateKey'; - -export class SetMeasurementTypeCommand extends RenderTargetCommand { - private readonly _measureType: MeasureType; - - // ================================================== - // CONSTRUCTOR - // ================================================== - - public constructor(measureType: MeasureType) { - super(); - this._measureType = measureType; - } - - // ================================================== - // OVERRIDES of BaseCommand - // ================================================== - - public override get icon(): string { - return getIconByMeasureType(this._measureType); - } - - public override get tooltip(): TranslateKey { - return getTooltipByMeasureType(this._measureType); - } - - public override get isEnabled(): boolean { - return this.measurementTool !== undefined; - } - - public override get isChecked(): boolean { - const { measurementTool } = this; - if (measurementTool === undefined) { - return false; - } - return measurementTool.measureType === this._measureType; - } - - protected override invokeCore(): boolean { - const { measurementTool } = this; - if (measurementTool === undefined) { - return false; - } - measurementTool.handleEscape(); - measurementTool.clearDragging(); - if (measurementTool.measureType === this._measureType) { - measurementTool.measureType = MeasureType.None; - } else { - measurementTool.measureType = this._measureType; - } - return true; - } - - public override equals(other: BaseCommand): boolean { - if (!(other instanceof SetMeasurementTypeCommand)) { - return false; - } - return this._measureType === other._measureType; - } - - // ================================================== - // INSTANCE METHODS - // ================================================== - - private get measurementTool(): MeasurementTool | undefined { - const activeTool = this.renderTarget.commandsController.activeTool; - if (!(activeTool instanceof MeasurementTool)) { - return undefined; - } - return activeTool; - } -} diff --git a/react-components/src/architecture/concrete/config/StoryBookConfig.ts b/react-components/src/architecture/concrete/config/StoryBookConfig.ts index 3bc83fb6b30..8253a92e1ce 100644 --- a/react-components/src/architecture/concrete/config/StoryBookConfig.ts +++ b/react-components/src/architecture/concrete/config/StoryBookConfig.ts @@ -11,13 +11,14 @@ import { UpdateTerrainCommand } from '../terrainDomainObject/UpdateTerrainComman import { FitViewCommand } from '../../base/concreteCommands/FitViewCommand'; import { SetAxisVisibleCommand } from '../axis/SetAxisVisibleCommand'; import { ExampleTool } from '../exampleDomainObject/ExampleTool'; -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'; +import { MeasurementTool } from '../measurments/MeasurementTool'; +import { CropBoxTool } from '../cropping/CropBoxTool'; export class StoryBookConfig extends BaseRevealConfig { // ================================================== @@ -39,6 +40,7 @@ export class StoryBookConfig extends BaseRevealConfig { undefined, new ExampleTool(), new MeasurementTool(), + new CropBoxTool(), undefined, new SetTerrainVisibleCommand(), new UpdateTerrainCommand() diff --git a/react-components/src/architecture/concrete/cropping/CropBoxDomainObject.ts b/react-components/src/architecture/concrete/cropping/CropBoxDomainObject.ts new file mode 100644 index 00000000000..d72a5b8ae12 --- /dev/null +++ b/react-components/src/architecture/concrete/cropping/CropBoxDomainObject.ts @@ -0,0 +1,81 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal'; +import { Changes } from '../../base/domainObjectsHelpers/Changes'; +import { type DomainObjectChange } from '../../base/domainObjectsHelpers/DomainObjectChange'; +import { BoxFace } from '../../base/utilities/box/BoxFace'; +import { BoxDomainObject } from '../box/BoxDomainObject'; +import { Color } from 'three'; + +export const MIN_BOX_SIZE = 0.01; + +export class CropBoxDomainObject extends BoxDomainObject { + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor() { + super(); + this.color = new Color(Color.NAMES.blue); + } + + // ================================================== + // OVERRIDES of DomainObject + // ================================================== + + public override get icon(): string { + return 'Crop'; + } + + public override get typeName(): string { + return 'Crop box'; + } + + protected override notifyCore(change: DomainObjectChange): void { + super.notifyCore(change); + + if (this.isUseAsCropBox) { + if (change.isChanged(Changes.deleted)) { + this.setUseAsCropBox(false); + } + if (change.isChanged(Changes.geometry)) { + this.setUseAsCropBox(true); + } + } else if (change.isChanged(Changes.selected) && this.isSelected) { + const root = this.rootDomainObject; + if (root !== undefined && root.renderTarget.isGlobalCropBoxActive) { + this.setUseAsCropBox(true); + } + } + } + // ================================================== + // INSTANCE METHODS: Others + // ================================================== + + public get isUseAsCropBox(): boolean { + const root = this.rootDomainObject; + if (root === undefined) { + return false; + } + return root.renderTarget.isGlobalCropBox(this); + } + + public setUseAsCropBox(use: boolean): void { + const root = this.rootDomainObject; + if (root === undefined) { + return; + } + if (!use) { + root.renderTarget.clearGlobalCropBox(); + } else { + const boundingBox = this.getBoundingBox(); + boundingBox.applyMatrix4(CDF_TO_VIEWER_TRANSFORMATION); + const matrix = this.getMatrix(); + matrix.premultiply(CDF_TO_VIEWER_TRANSFORMATION); + const planes = BoxFace.createClippingPlanes(matrix); + root.renderTarget.setGlobalCropBox(planes, boundingBox, this); + } + } +} diff --git a/react-components/src/architecture/concrete/cropping/CropBoxTool.ts b/react-components/src/architecture/concrete/cropping/CropBoxTool.ts new file mode 100644 index 00000000000..016137ad737 --- /dev/null +++ b/react-components/src/architecture/concrete/cropping/CropBoxTool.ts @@ -0,0 +1,72 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { type BaseCommand } from '../../base/commands/BaseCommand'; +import { type BaseCreator } from '../../base/domainObjectsHelpers/BaseCreator'; +import { type DomainObject } from '../../base/domainObjects/DomainObject'; +import { type TranslateKey } from '../../base/utilities/TranslateKey'; +import { BoxOrLineEditTool } from '../box/BoxOrLineEditTool'; +import { type PrimitiveType } from '../box/PrimitiveType'; +import { BoxCreator } from '../box/BoxCreator'; +import { CropBoxDomainObject } from './CropBoxDomainObject'; +import { SetCropBoxCommand } from './SetCropBoxCommand'; + +export class CropBoxTool extends BoxOrLineEditTool { + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor() { + super(); + } + + // ================================================== + // OVERRIDES of BaseCommand + // ================================================== + + public override get icon(): string { + return 'Crop'; + } + + public override get tooltip(): TranslateKey { + return { key: 'CROP_BOX', fallback: 'Create or edit crop box' }; + } + + public override getToolbar(): Array { + return [new SetCropBoxCommand()]; + } + + // ================================================== + // OVERRIDES of BaseTool + // ================================================== + + public override onActivate(): void { + super.onActivate(); + this.setAllVisible(true); + } + + public override onDeactivate(): void { + super.onDeactivate(); + this.setAllVisible(false); + } + + // ================================================== + // OVERRIDES of BaseEditTool + // ================================================== + + protected override canBeSelected(domainObject: DomainObject): boolean { + if (domainObject instanceof CropBoxDomainObject) { + return true; + } + return false; + } + + // ================================================== + // OVERRIDES of BoxOrLineEditTool + // ================================================== + + protected override createCreator(_primitiveType: PrimitiveType): BaseCreator | undefined { + return new BoxCreator(new CropBoxDomainObject()); + } +} diff --git a/react-components/src/architecture/concrete/boxDomainObject/SetCropBoxCommand.ts b/react-components/src/architecture/concrete/cropping/SetCropBoxCommand.ts similarity index 68% rename from react-components/src/architecture/concrete/boxDomainObject/SetCropBoxCommand.ts rename to react-components/src/architecture/concrete/cropping/SetCropBoxCommand.ts index 3cb948b77ac..3a1612bfcef 100644 --- a/react-components/src/architecture/concrete/boxDomainObject/SetCropBoxCommand.ts +++ b/react-components/src/architecture/concrete/cropping/SetCropBoxCommand.ts @@ -4,10 +4,7 @@ import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; import { type TranslateKey } from '../../base/utilities/TranslateKey'; -import { MeasureBoxDomainObject } from './MeasureBoxDomainObject'; -import { MeasureType } from './MeasureType'; - -// Experimental code for crop box +import { CropBoxDomainObject } from './CropBoxDomainObject'; export class SetCropBoxCommand extends RenderTargetCommand { // ================================================== @@ -26,7 +23,7 @@ export class SetCropBoxCommand extends RenderTargetCommand { if (this.renderTarget.isGlobalCropBoxActive) { return true; } - return this.getMeasureBoxDomainObject() !== undefined; + return this.getCropBoxDomainObject() !== undefined; } public override get isChecked(): boolean { @@ -35,7 +32,7 @@ export class SetCropBoxCommand extends RenderTargetCommand { protected override invokeCore(): boolean { const { renderTarget } = this; - const domainObject = this.getMeasureBoxDomainObject(); + const domainObject = this.getCropBoxDomainObject(); if (domainObject === undefined || this.renderTarget.isGlobalCropBoxActive) { renderTarget.clearGlobalCropBox(); return false; @@ -53,14 +50,7 @@ export class SetCropBoxCommand extends RenderTargetCommand { // INSTANCE METHODS // ================================================== - private getMeasureBoxDomainObject(): MeasureBoxDomainObject | undefined { - const domainObject = this.rootDomainObject.getSelectedDescendantByType(MeasureBoxDomainObject); - if (domainObject === undefined) { - return undefined; - } - if (domainObject.measureType !== MeasureType.Volume) { - return undefined; - } - return domainObject; + private getCropBoxDomainObject(): CropBoxDomainObject | undefined { + return this.rootDomainObject.getSelectedDescendantByType(CropBoxDomainObject); } } diff --git a/react-components/src/architecture/concrete/measurments/MeasureBoxDomainObject.ts b/react-components/src/architecture/concrete/measurments/MeasureBoxDomainObject.ts new file mode 100644 index 00000000000..f7f62bb05b7 --- /dev/null +++ b/react-components/src/architecture/concrete/measurments/MeasureBoxDomainObject.ts @@ -0,0 +1,42 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { PrimitiveType } from '../box/PrimitiveType'; +import { BoxDomainObject } from '../box/BoxDomainObject'; +import { Color } from 'three'; +import { getIconByPrimitiveType } from './getIconByPrimitiveType'; + +export const MIN_BOX_SIZE = 0.01; + +export class MeasureBoxDomainObject extends BoxDomainObject { + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor(primitiveType: PrimitiveType) { + super(primitiveType); + this.color = new Color(Color.NAMES.magenta); + } + + // ================================================== + // OVERRIDES of DomainObject + // ================================================== + + public override get icon(): string { + return getIconByPrimitiveType(this.primitiveType); + } + + public override get typeName(): string { + switch (this.primitiveType) { + case PrimitiveType.HorizontalArea: + return 'Horizontal area'; + case PrimitiveType.VerticalArea: + return 'Vertical area'; + case PrimitiveType.Box: + return 'Volume'; + default: + throw new Error('Unknown PrimitiveType'); + } + } +} diff --git a/react-components/src/architecture/concrete/measurments/MeasureLineDomainObject.ts b/react-components/src/architecture/concrete/measurments/MeasureLineDomainObject.ts new file mode 100644 index 00000000000..6079a70811c --- /dev/null +++ b/react-components/src/architecture/concrete/measurments/MeasureLineDomainObject.ts @@ -0,0 +1,40 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { PrimitiveType } from '../box/PrimitiveType'; +import { LineDomainObject } from '../box/LineDomainObject'; +import { getIconByPrimitiveType } from './getIconByPrimitiveType'; +import { Color } from 'three'; + +export class MeasureLineDomainObject extends LineDomainObject { + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor(primitiveType: PrimitiveType) { + super(primitiveType); + this.color = new Color(Color.NAMES.red); + } + + // ================================================== + // OVERRIDES of DomainObject + // ================================================== + + public override get icon(): string { + return getIconByPrimitiveType(this.primitiveType); + } + + public override get typeName(): string { + switch (this.primitiveType) { + case PrimitiveType.Line: + return 'Line'; + case PrimitiveType.Polyline: + return 'Polyline'; + case PrimitiveType.Polygon: + return 'Polygon'; + default: + throw new Error('Unknown PrimitiveType'); + } + } +} diff --git a/react-components/src/architecture/concrete/measurments/MeasurementTool.ts b/react-components/src/architecture/concrete/measurments/MeasurementTool.ts new file mode 100644 index 00000000000..c90f83889f5 --- /dev/null +++ b/react-components/src/architecture/concrete/measurments/MeasurementTool.ts @@ -0,0 +1,98 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { type BaseCommand } from '../../base/commands/BaseCommand'; +import { type BaseCreator } from '../../base/domainObjectsHelpers/BaseCreator'; +import { type DomainObject } from '../../base/domainObjects/DomainObject'; +import { ShowMeasurementsOnTopCommand } from './ShowMeasurementsOnTopCommand'; +import { SetMeasurementTypeCommand } from './SetMeasurementTypeCommand'; +import { type TranslateKey } from '../../base/utilities/TranslateKey'; +import { ToggleMetricUnitsCommand } from '../../base/concreteCommands/ToggleMetricUnitsCommand'; +import { BoxOrLineEditTool } from '../box/BoxOrLineEditTool'; +import { MeasureLineDomainObject } from './MeasureLineDomainObject'; +import { MeasureBoxDomainObject } from './MeasureBoxDomainObject'; +import { PrimitiveType } from '../box/PrimitiveType'; +import { BoxCreator } from '../box/BoxCreator'; +import { LineCreator } from '../box/LineCreator'; + +export class MeasurementTool extends BoxOrLineEditTool { + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor() { + super(PrimitiveType.None); + } + + // ================================================== + // OVERRIDES of BaseCommand + // ================================================== + + public override get icon(): string { + return 'Ruler'; + } + + public override get tooltip(): TranslateKey { + return { key: 'MEASUREMENTS', fallback: 'Measurements' }; + } + + public override getToolbar(): Array { + return [ + new SetMeasurementTypeCommand(PrimitiveType.Line), + new SetMeasurementTypeCommand(PrimitiveType.Polyline), + new SetMeasurementTypeCommand(PrimitiveType.Polygon), + new SetMeasurementTypeCommand(PrimitiveType.HorizontalArea), + new SetMeasurementTypeCommand(PrimitiveType.VerticalArea), + new SetMeasurementTypeCommand(PrimitiveType.Box), + undefined, // Separator + new ToggleMetricUnitsCommand(), + new ShowMeasurementsOnTopCommand() + ]; + } + + // ================================================== + // OVERRIDES of BaseTool + // ================================================== + + public override onActivate(): void { + super.onActivate(); + this.setAllVisible(true); + } + + public override onDeactivate(): void { + super.onDeactivate(); + this.setAllVisible(false); + } + + // ================================================== + // OVERRIDES of BaseEditTool + // ================================================== + + protected override canBeSelected(domainObject: DomainObject): boolean { + return ( + domainObject instanceof MeasureBoxDomainObject || + domainObject instanceof MeasureLineDomainObject + ); + } + + // ================================================== + // OVERRIDES of BoxOrLineEditTool + // ================================================== + + protected override createCreator(primitiveType: PrimitiveType): BaseCreator | undefined { + switch (primitiveType) { + case PrimitiveType.Line: + case PrimitiveType.Polyline: + case PrimitiveType.Polygon: + return new LineCreator(new MeasureLineDomainObject(primitiveType)); + + case PrimitiveType.HorizontalArea: + case PrimitiveType.VerticalArea: + case PrimitiveType.Box: + return new BoxCreator(new MeasureBoxDomainObject(primitiveType)); + default: + return undefined; + } + } +} diff --git a/react-components/src/architecture/concrete/measurments/SetMeasurementTypeCommand.ts b/react-components/src/architecture/concrete/measurments/SetMeasurementTypeCommand.ts new file mode 100644 index 00000000000..566e807c80d --- /dev/null +++ b/react-components/src/architecture/concrete/measurments/SetMeasurementTypeCommand.ts @@ -0,0 +1,124 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { RenderTargetCommand } from '../../base/commands/RenderTargetCommand'; +import { type BaseCommand } from '../../base/commands/BaseCommand'; +import { PrimitiveType } from '../box/PrimitiveType'; +import { getIconByPrimitiveType } from './getIconByPrimitiveType'; +import { type TranslateKey } from '../../base/utilities/TranslateKey'; +import { MeasurementTool } from './MeasurementTool'; + +export class SetMeasurementTypeCommand extends RenderTargetCommand { + private readonly _measureType: PrimitiveType; + + // ================================================== + // CONSTRUCTOR + // ================================================== + + public constructor(measureType: PrimitiveType) { + super(); + this._measureType = measureType; + } + + // ================================================== + // OVERRIDES of BaseCommand + // ================================================== + + public override get icon(): string { + return getIconByPrimitiveType(this._measureType); + } + + public override get tooltip(): TranslateKey { + return getTooltipByPrimitiveType(this._measureType); + } + + public override get isEnabled(): boolean { + return this.measurementTool !== undefined; + } + + public override get isChecked(): boolean { + const { measurementTool } = this; + if (measurementTool === undefined) { + return false; + } + return measurementTool.primitiveType === this._measureType; + } + + protected override invokeCore(): boolean { + const { measurementTool } = this; + if (measurementTool === undefined) { + return false; + } + measurementTool.handleEscape(); + measurementTool.clearDragging(); + if (measurementTool.primitiveType === this._measureType) { + measurementTool.primitiveType = PrimitiveType.None; + } else { + measurementTool.primitiveType = this._measureType; + } + return true; + } + + public override equals(other: BaseCommand): boolean { + if (!(other instanceof SetMeasurementTypeCommand)) { + return false; + } + return this._measureType === other._measureType; + } + + // ================================================== + // INSTANCE METHODS + // ================================================== + + private get measurementTool(): MeasurementTool | undefined { + const activeTool = this.renderTarget.commandsController.activeTool; + if (!(activeTool instanceof MeasurementTool)) { + return undefined; + } + return activeTool; + } +} + +// ================================================== +// PRIMATE FUNCTIONS +// ================================================== + +function getTooltipByPrimitiveType(primitiveType: PrimitiveType): TranslateKey { + switch (primitiveType) { + case PrimitiveType.Line: + return { + key: 'MEASUREMENTS_ADD_LINE', + fallback: 'Measure distance between two points. Click at the start point and the end point.' + }; + case PrimitiveType.Polyline: + return { + key: 'MEASUREMENTS_ADD_POLYLINE', + fallback: + 'Measure the length of a continuous polyline. Click at any number of points and end with Esc.' + }; + case PrimitiveType.Polygon: + return { + key: 'MEASUREMENTS_ADD_POLYGON', + fallback: 'Measure an area of a polygon. Click at least 3 points and end with Esc.' + }; + case PrimitiveType.VerticalArea: + return { + key: 'MEASUREMENTS_ADD_VERTICAL_AREA', + fallback: 'Measure rectangular vertical Area. Click at two points in a vertical plan.' + }; + case PrimitiveType.HorizontalArea: + return { + key: 'MEASUREMENTS_ADD_HORIZONTAL_AREA', + fallback: 'Measure rectangular horizontal Area. Click at three points in a horizontal plan.' + }; + case PrimitiveType.Box: + return { + key: 'MEASUREMENTS_ADD_VOLUME', + fallback: + 'Measure volume of a box. Click at three points in a horizontal plan and the fourth to give it height.' + }; + default: + throw new Error('Unknown PrimitiveType'); + } +} diff --git a/react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts b/react-components/src/architecture/concrete/measurments/ShowMeasurementsOnTopCommand.ts similarity index 100% rename from react-components/src/architecture/concrete/boxDomainObject/ShowMeasurementsOnTopCommand.ts rename to react-components/src/architecture/concrete/measurments/ShowMeasurementsOnTopCommand.ts diff --git a/react-components/src/architecture/concrete/measurments/getIconByPrimitiveType.ts b/react-components/src/architecture/concrete/measurments/getIconByPrimitiveType.ts new file mode 100644 index 00000000000..1b1a266fc8e --- /dev/null +++ b/react-components/src/architecture/concrete/measurments/getIconByPrimitiveType.ts @@ -0,0 +1,24 @@ +/*! + * Copyright 2024 Cognite AS + */ + +import { PrimitiveType } from '../box/PrimitiveType'; + +export function getIconByPrimitiveType(primitiveType: PrimitiveType): string { + switch (primitiveType) { + case PrimitiveType.Line: + return 'VectorLine'; + case PrimitiveType.Polyline: + return 'VectorZigzag'; + case PrimitiveType.Polygon: + return 'Polygon'; + case PrimitiveType.HorizontalArea: + return 'FrameTool'; + case PrimitiveType.VerticalArea: + return 'Perspective'; + case PrimitiveType.Box: + return 'Cube'; + default: + throw new Error('Unknown PrimitiveType'); + } +} diff --git a/react-components/src/components/Architecture/CommandButton.tsx b/react-components/src/components/Architecture/CommandButton.tsx index 3e884ff5df2..91978fc1d92 100644 --- a/react-components/src/components/Architecture/CommandButton.tsx +++ b/react-components/src/components/Architecture/CommandButton.tsx @@ -44,7 +44,7 @@ export const CommandButton = ({ const [isChecked, setChecked] = useState(false); const [isEnabled, setEnabled] = useState(true); const [isVisible, setVisible] = useState(true); - const [uniqueIndex, setUniqueIndex] = useState(0); + const [uniqueId, setUniqueId] = useState(0); const [icon, setIcon] = useState('Copy'); useEffect(() => { @@ -52,7 +52,7 @@ export const CommandButton = ({ setChecked(command.isChecked); setEnabled(command.isEnabled); setVisible(command.isVisible); - setUniqueIndex(command._uniqueIndex); + setUniqueId(command.uniqueId); setIcon(command.icon as IconType); } update(newCommand); @@ -67,7 +67,7 @@ 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) + // This was the only way it went through compiler: (more button types will be added in the future) const type = newCommand.buttonType; if (type !== 'ghost' && type !== 'ghost-destructive') { return <>; @@ -77,7 +77,7 @@ export const CommandButton = ({