Skip to content

Commit

Permalink
Merge branch 'master' into np/undo
Browse files Browse the repository at this point in the history
  • Loading branch information
nilscognite committed Jun 26, 2024
2 parents 3b35e23 + 80328b0 commit d75cc54
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { SetClipTypeCommand } from './commands/SetClipTypeCommand';
import { PlaneCreator } from '../primitives/plane/PlaneCreator';
import { SliceDomainObject } from './SliceDomainObject';
import { UndoCommand } from '../../base/concreteCommands/UndoCommand';
import { NextOrPrevClippingCommand } from './commands/NextClippingCommand';

export class ClipTool extends PrimitiveEditTool {
// ==================================================
Expand Down Expand Up @@ -49,8 +50,10 @@ export class ClipTool extends PrimitiveEditTool {
undefined, // Separator
new UndoCommand(),
new ApplyClipCommand(),
new ShowClippingOnTopCommand(),
new ShowAllClippingCommand()
new NextOrPrevClippingCommand(false),
new NextOrPrevClippingCommand(true),
new ShowAllClippingCommand(),
new ShowClippingOnTopCommand()
];
}

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

import { type BaseCommand } from '../../../base/commands/BaseCommand';
import { RenderTargetCommand } from '../../../base/commands/RenderTargetCommand';
import { type TranslateKey } from '../../../base/utilities/TranslateKey';
import { CropBoxDomainObject } from '../CropBoxDomainObject';
import { SliceDomainObject } from '../SliceDomainObject';
import { ApplyClipCommand } from './ApplyClipCommand';

export class NextOrPrevClippingCommand extends RenderTargetCommand {
private readonly _next: boolean;

// ==================================================
// CONSTRUCTORS
// ==================================================

public constructor(next: boolean) {
super();
this._next = next;
}

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

public override get tooltip(): TranslateKey {
if (this._next) {
return {
key: 'CLIP_NEXT',
fallback: 'Set the next crop box or slicing plane as global clipping'
};
} else {
return {
key: 'CLIP_PREV',
fallback: 'Set the previous crop box or slicing plane as global clipping'
};
}
}

public override get icon(): string {
return this._next ? 'ArrowRight' : 'ArrowLeft';
}

public override get isEnabled(): boolean {
if (!this.renderTarget.isGlobalClippingActive) {
return false;
}
const minimumCount = this._next ? 2 : 3; // Don't need both buttons if it is less than 3
const { rootDomainObject } = this;
// Require at least two crop boxes or one crop box and one slice
let count = 0;
for (const domainObject of rootDomainObject.getDescendants()) {
if (domainObject instanceof CropBoxDomainObject) {
count++;
if (count >= minimumCount) {
return true; // Optimization
}
}
}
if (rootDomainObject.getDescendantByType(SliceDomainObject) !== undefined) {
count++;
}
return count >= minimumCount;
}

public override equals(other: BaseCommand): boolean {
if (!(other instanceof NextOrPrevClippingCommand)) {
return false;
}
return this._next === other._next;
}

protected override invokeCore(): boolean {
// This code treat the slicing planes as one single group, along with all the crop boxes.
// The next selected crop box or slicing planes will be used as clipping.
const array = this.createCropBoxesAndSliceArray();
if (array.length <= 1) {
return false;
}
const selectedIndex = array.findIndex((domainObject) => domainObject.isSelected);
if (selectedIndex === undefined) {
return false;
}
const nextIndex = this.getNextIndex(selectedIndex, array.length);
this.setAllInvisibleAndDeselected(array, nextIndex);

// Take the next crop box or slicing planes and use it as clipping
const nextCropBoxOrSlice = array[nextIndex];
this.setVisibleAndSelected(nextCropBoxOrSlice, true);
if (nextCropBoxOrSlice instanceof CropBoxDomainObject) {
nextCropBoxOrSlice.setThisAsGlobalCropBox();
} else {
ApplyClipCommand.setClippingPlanes(this.rootDomainObject);
}
this.renderTarget.fitView();
return true;
}

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

private createCropBoxesAndSliceArray(): Array<CropBoxDomainObject | SliceDomainObject> {
const { rootDomainObject } = this;
// Build the array of crop boxes and at least one slice
const array = new Array<CropBoxDomainObject | SliceDomainObject>();
for (const cropBox of rootDomainObject.getDescendantsByType(CropBoxDomainObject)) {
array.push(cropBox);
}
// Take the selected slice, otherwise take the first one
const selectedSlice = rootDomainObject.getSelectedDescendantByType(SliceDomainObject);
if (selectedSlice !== undefined) {
array.push(selectedSlice);
} else {
const sliceDomainObject = rootDomainObject.getDescendantByType(SliceDomainObject);
if (sliceDomainObject !== undefined) {
array.push(sliceDomainObject);
}
}
return array;
}

private getNextIndex(selectedIndex: number, arrayLength: number): number {
const increment = this._next ? 1 : -1;
const nextIndex = selectedIndex + increment;
if (nextIndex < 0) {
return arrayLength - 1;
} else if (nextIndex >= arrayLength) {
return 0;
}
return nextIndex;
}

private setAllInvisibleAndDeselected(
array: Array<CropBoxDomainObject | SliceDomainObject>,
exceptIndex: number
): void {
for (let i = 0; i < array.length; i++) {
if (i !== exceptIndex) {
this.setVisibleAndSelected(array[i], false);
}
}
}

private setVisibleAndSelected(
domainObject: CropBoxDomainObject | SliceDomainObject,
value: boolean
): void {
domainObject.setSelectedInteractive(value);
if (domainObject instanceof SliceDomainObject) {
this.setAllSliceDomainObjectsVisible(value);
} else {
domainObject.setVisibleInteractive(value, this.renderTarget);
}
}

private setAllSliceDomainObjectsVisible(visible: boolean): void {
const { rootDomainObject } = this;
for (const sliceDomainObject of rootDomainObject.getDescendantsByType(SliceDomainObject)) {
sliceDomainObject.setVisibleInteractive(visible, this.renderTarget);
}
}

public getCropBoxesAndSliceCount(): number {
const { rootDomainObject } = this;
// Require at least two crop boxes or one crop box and one slice
let count = 0;
for (const domainObject of rootDomainObject.getDescendants()) {
if (domainObject instanceof CropBoxDomainObject) {
count++;
}
}
if (rootDomainObject.getDescendantByType(SliceDomainObject) !== undefined) {
count++;
}
return count;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class ExampleTool extends BaseEditTool {
// ==================================================

public override onKey(event: KeyboardEvent, down: boolean): void {
if (down && event.key === 'Delete') {
if (down && (event.key === 'Delete' || event.key === 'Backspace')) {
const domainObject = this.getSelected();
if (domainObject instanceof ExampleDomainObject) {
this.addTransaction(domainObject.createTransaction(Changes.deleted));
Expand Down Expand Up @@ -65,7 +65,7 @@ export class ExampleTool extends BaseEditTool {
hsl.h = (hsl.h + Math.sign(delta) * 0.02) % 1;
domainObject.color.setHSL(hsl.h, hsl.s, hsl.l);
domainObject.notify(Changes.color);
} else if (event.ctrlKey) {
} else if (event.ctrlKey || event.metaKey) {
// Change opacity
this.addTransaction(domainObject.createTransaction(Changes.renderStyle));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export abstract class PrimitiveEditTool extends BaseEditTool {
}

public override onKey(event: KeyboardEvent, down: boolean): void {
if (down && event.key === 'Delete') {
if (down && (event.key === 'Delete' || event.key === 'Backspace')) {
const domainObject = this.getSelected();
if (domainObject !== undefined) {
this.addTransaction(domainObject.createTransaction(Changes.deleted));
Expand Down

0 comments on commit d75cc54

Please sign in to comment.