Skip to content

Commit

Permalink
react-component(feat): Add crop box, slicing and tool, generalize box…
Browse files Browse the repository at this point in the history
… concept (#4594)

* First commit

* Generalized box and lines

* More generalize

* Further work

* Fix crop box

* Some fixes

* Move files again

* Move files

* Reuse code

* adjustments

* After add constrains

* Fixing cropping

* Fix typename

* Working good now

* Update PlaneDomainObject.ts

* Fix updating

* Fix updating

* Make a general bounding box

* Fixes

* make flipping consistent

* Change name

* Reorganization

* Fixing

* more fixes

* Fix icon

* Fixing bounding box

* Smaller fixing

* Fix flaes

* Update RevealButtons.tsx

* Fix updating of pending

* Update CropBoxDomainObject.ts

* Update comments

* Example uses ClipPlane, differentiate Left/Right button, contrained resizing

* Update BoxDragger.ts

* Fixes according to review

* Fix unit in box dragging
  • Loading branch information
nilscognite authored Jun 17, 2024
1 parent 1a98967 commit 14f2d90
Show file tree
Hide file tree
Showing 95 changed files with 3,106 additions and 1,231 deletions.
20 changes: 12 additions & 8 deletions react-components/src/architecture/base/commands/BaseCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -48,7 +52,7 @@ export abstract class BaseCommand {
}

public get tooltip(): TranslateKey {
return { key: '' };
return { fallback: '' };
}

public get icon(): string {
Expand Down
81 changes: 48 additions & 33 deletions react-components/src/architecture/base/commands/BaseEditTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
* Copyright 2024 Cognite AS
*/

import { NavigationTool } from './NavigationTool';
import { NavigationTool } from '../concreteCommands/NavigationTool';
import { isDomainObjectIntersection } from '../domainObjectsHelpers/DomainObjectIntersection';
import { type BaseDragger } from '../domainObjectsHelpers/BaseDragger';
import { VisualDomainObject } from '../domainObjects/VisualDomainObject';
import { type AnyIntersection, CDF_TO_VIEWER_TRANSFORMATION } from '@cognite/reveal';
import { DomainObjectPanelUpdater } from '../reactUpdaters/DomainObjectPanelUpdater';

/**
* The `BaseEditTool` class is an abstract class that extends the `NavigationTool` class.
Expand Down Expand Up @@ -35,38 +36,44 @@ export abstract class BaseEditTool extends NavigationTool {
this._dragger = undefined;
}

public override async onPointerDown(event: PointerEvent, leftButton: boolean): Promise<void> {
public override async onLeftPointerDown(event: PointerEvent): Promise<void> {
this._dragger = await this.createDragger(event);
if (this._dragger === undefined) {
await super.onPointerDown(event, leftButton);
await super.onLeftPointerDown(event);
return;
}
this._dragger.onPointerDown(event);
this.deselectAll(this._dragger.domainObject);
this._dragger.domainObject.setSelectedInteractive(true);
}

public override async onPointerDrag(event: PointerEvent, leftButton: boolean): Promise<void> {
public override async onLeftPointerDrag(event: PointerEvent): Promise<void> {
if (this._dragger === undefined) {
await super.onPointerDrag(event, leftButton);
await super.onLeftPointerDrag(event);
return;
}
const ray = this.getRay(event, true);
this._dragger.onPointerDrag(event, ray);
}

public override async onPointerUp(event: PointerEvent, leftButton: boolean): Promise<void> {
public override async onLeftPointerUp(event: PointerEvent): Promise<void> {
if (this._dragger === undefined) {
await super.onPointerUp(event, leftButton);
await super.onLeftPointerUp(event);
} else {
this._dragger.onPointerUp(event);
this._dragger = undefined;
}
}

public override onActivate(): void {
super.onActivate();
const selected = this.getSelected();
DomainObjectPanelUpdater.show(selected);
}

public override onDeactivate(): void {
super.onDeactivate();
this.deselectAll();
DomainObjectPanelUpdater.hide();
}

// ==================================================
Expand Down Expand Up @@ -111,22 +118,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<VisualDomainObject> {
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;
}

/**
Expand All @@ -135,35 +149,36 @@ export abstract class BaseEditTool extends NavigationTool {
* @returns A generator that yields each selected domain object.
*/
protected *getAllSelected(): Generator<VisualDomainObject> {
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;
}

/**
Expand Down
20 changes: 16 additions & 4 deletions react-components/src/architecture/base/commands/BaseTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export abstract class BaseTool extends RenderTargetCommand {
}

public getToolbarStyle(): PopupStyle {
// Override this to place the the toolbar
// Override this to place the toolbar
// Default lower left corner
return new PopupStyle({ bottom: 0, left: 0 });
}
Expand Down Expand Up @@ -89,15 +89,27 @@ export abstract class BaseTool extends RenderTargetCommand {
await Promise.resolve();
}

public async onPointerDown(_event: PointerEvent, _leftButton: boolean): Promise<void> {
public async onLeftPointerDown(_event: PointerEvent): Promise<void> {
await Promise.resolve();
}

public async onPointerDrag(_event: PointerEvent, _leftButton: boolean): Promise<void> {
public async onLeftPointerDrag(_event: PointerEvent): Promise<void> {
await Promise.resolve();
}

public async onPointerUp(_event: PointerEvent, _leftButton: boolean): Promise<void> {
public async onLeftPointerUp(_event: PointerEvent): Promise<void> {
await Promise.resolve();
}

public async onRightPointerDown(_event: PointerEvent): Promise<void> {
await Promise.resolve();
}

public async onRightPointerDrag(_event: PointerEvent): Promise<void> {
await Promise.resolve();
}

public async onRightPointerUp(_event: PointerEvent): Promise<void> {
await Promise.resolve();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*!
* Copyright 2024 Cognite AS
*/

import { type DomainObject } from '../domainObjects/DomainObject';
import { BaseCommand } from './BaseCommand';

export abstract class DomainObjectCommand<Type extends DomainObject> extends BaseCommand {
// ==================================================
// INSTANCE FIELDS
// ==================================================

protected readonly _domainObject: Type;

// ==================================================
// CONSTRUCTOR
// ==================================================

public constructor(domainObject: Type) {
super();
this._domainObject = domainObject;
}

// ==================================================
// OVERRIDES
// ==================================================

public override get hasData(): boolean {
return true;
}
}
42 changes: 42 additions & 0 deletions react-components/src/architecture/base/commands/InstanceCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*!
* Copyright 2024 Cognite AS
*/

import { RenderTargetCommand } from './RenderTargetCommand';
import { type DomainObject } from '../domainObjects/DomainObject';

export abstract class InstanceCommand extends RenderTargetCommand {
// ==================================================
// OVERRIDES
// ==================================================

public override get isEnabled(): boolean {
return this.anyInstances;
}

// ==================================================
// VIRTUAL METHODS
// ==================================================

protected abstract isInstance(domainObject: DomainObject): boolean;

// ==================================================
// INSTANCE METHODS
// ==================================================

protected get anyInstances(): boolean {
return this.getFirstInstance() !== undefined;
}

protected getFirstInstance(): DomainObject | undefined {
return this.getInstances().next().value;
}

protected *getInstances(): Generator<DomainObject> {
for (const domainObject of this.rootDomainObject.getDescendants()) {
if (this.isInstance(domainObject)) {
yield domainObject;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*!
* Copyright 2024 Cognite AS
*/

import { type TranslateKey } from '../utilities/TranslateKey';
import { InstanceCommand } from './InstanceCommand';

export abstract class ShowAllDomainObjectsCommand extends InstanceCommand {
// ==================================================
// OVERRIDES
// ==================================================

public override get tooltip(): TranslateKey {
return { fallback: 'Show or hide' };
}

public override get icon(): string {
return 'EyeShow';
}

public override get isChecked(): boolean {
return this.isAnyVisible();
}

protected override invokeCore(): boolean {
const isVisible = this.isAnyVisible();
for (const domainObject of this.getInstances()) {
domainObject.setVisibleInteractive(!isVisible, this.renderTarget);
}
return true;
}

// ==================================================
// INSTANCE METHODS
// ==================================================

private isAnyVisible(): boolean {
for (const domainObject of this.getInstances()) {
if (domainObject.isVisible(this.renderTarget)) {
return true;
}
}
return false;
}
}
Loading

0 comments on commit 14f2d90

Please sign in to comment.