Skip to content

Commit

Permalink
#4861 - Introducing multi-tail arrows (#5376)
Browse files Browse the repository at this point in the history
Co-authored-by: Roman Rodionov <[email protected]>
Co-authored-by: Alexey Girin <[email protected]>
  • Loading branch information
3 people authored and Guch1g0v committed Oct 17, 2024
1 parent 8ff05bd commit e2aa8de
Show file tree
Hide file tree
Showing 96 changed files with 2,831 additions and 471 deletions.
12 changes: 11 additions & 1 deletion documentation/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ This tool flips the objects horizontally or vertically.

To draw/edit reactions you can:

- draw reagents and products as described above;
- draw reagents and products as described above
- use options of the _Reaction Arrow Tool_ <img src = images/34_reacarrow_icon.png width = "42" /> to draw an
arrow. Select the arrow needed from the list <img src = images/35_reacarrows_menu.png height = "42" />
- draw pluses in the reaction equation using the _Reaction Plus Tool_ <img src = images/38_reactplus_icon.png width = "42" />
Expand All @@ -429,6 +429,16 @@ To draw/edit reactions you can:

3 – Reaction Unmapping Tool

# Drawing pathway reactions

To draw pathway reactions you can:
* draw reagents and products as described above
* use <img src = images/70_add_multitailed-arrow.png width = "42" /> _Multi-Tailed Arrow Tool_ option from _Reaction Arrow Tool_
* click anywhere on the canvas to add new multi-tailed arrow
* adjust the length of the head or tail by grabbing its end and moving the cursor left and right
* reposition the head or tail vertically by grabbing its end and moving the cursos up and down


# Drawing graphical objects

To draw graphical objects click the arrow on the _Shape Ellipse_ tool <img src = images/39_objects_icon.png width = "42" /> in the Tools palette
Expand Down
Binary file modified documentation/images/35_reacarrows_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/70_add_multitailed-arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ const formatsForSave = [
},
];

const OFFSET_FROM_ARROW = 30;

test.describe('Plus and Arrows tools ', () => {
const modifier = getControlModifier();
const CANVAS_CLICK_X = 300;
Expand Down Expand Up @@ -369,8 +371,8 @@ test.describe('Plus and Arrows tools ', () => {
});

test('Select the reaction arrow and move it', async ({ page }) => {
await page.mouse.move(point.x + 60, point.y);
await dragMouseTo(point.x + 60, point.y - 40, page);
await page.mouse.move(point.x + OFFSET_FROM_ARROW, point.y);
await dragMouseTo(point.x + OFFSET_FROM_ARROW, point.y - 40, page);
});

test('Select the reaction arrow with any reaction component(s) and move them', async ({
Expand All @@ -392,7 +394,7 @@ test.describe('Plus and Arrows tools ', () => {
page,
}) => {
await waitForRender(page, async () => {
await page.mouse.click(point.x + 60, point.y);
await page.mouse.click(point.x + OFFSET_FROM_ARROW, point.y);
});
await cutToClipboardByKeyboard(page);
await pasteFromClipboardByKeyboard(page, { delay: INPUT_DELAY });
Expand All @@ -415,7 +417,7 @@ test.describe('Plus and Arrows tools ', () => {
page,
}) => {
await waitForRender(page, async () => {
await page.mouse.click(point.x + 60, point.y);
await page.mouse.click(point.x + OFFSET_FROM_ARROW, point.y);
});

await copyToClipboardByKeyboard(page);
Expand All @@ -429,7 +431,7 @@ test.describe('Plus and Arrows tools ', () => {
page,
}) => {
await selectLeftPanelButton(LeftPanelButton.Erase, page);
await clickOnTheCanvas(page, -60, 0);
await clickOnTheCanvas(page, -OFFSET_FROM_ARROW, 0);
await takeEditorScreenshot(page);
await selectTopPanelButton(TopPanelButton.Undo, page);
await takeEditorScreenshot(page);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
saveToFile,
waitForLoad,
waitForPageInit,
moveMouseAway,
} from '@utils';
import { clickOnFileFormatDropdown, getRxn } from '@utils/formats';

Expand Down Expand Up @@ -211,6 +212,7 @@ test.describe('Reagents RXN format', () => {
page,
);
await clickInTheMiddleOfTheScreen(page);
await moveMouseAway(page);
});

test('Open from file in "RXN V3000" format', async ({ page }) => {
Expand All @@ -223,6 +225,7 @@ test.describe('Reagents RXN format', () => {
page,
);
await clickInTheMiddleOfTheScreen(page);
await moveMouseAway(page);
});

test('Paste from clipboard in "RXN V2000" format', async ({ page }) => {
Expand All @@ -235,6 +238,7 @@ test.describe('Reagents RXN format', () => {
FILE_TEST_DATA.benzeneArrowBenzeneReagentHclV2000,
);
await clickInTheMiddleOfTheScreen(page);
await moveMouseAway(page);
});

test('Paste from clipboard in "RXN V3000" format', async ({ page }) => {
Expand All @@ -261,5 +265,6 @@ test.describe('Reagents RXN format', () => {
page,
);
await clickInTheMiddleOfTheScreen(page);
await moveMouseAway(page);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,9 @@ test.describe('R-Group Label Tool', () => {
const y = 200;
await openFileAndAddToCanvas('Rxn-V2000/chain-with-r-group.rxn', page);
await copyAndPaste(page);
await page.mouse.click(x, y);
await waitForRender(page, async () => {
await page.mouse.click(x, y);
});
});

test('Cut/Paste actions Structure with R-Group label', async ({ page }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
cutToClipboardByKeyboard,
copyToClipboardByKeyboard,
pasteFromClipboardByKeyboard,
moveMouseAway,
} from '@utils';

const CANVAS_CLICK_X = 500;
Expand Down Expand Up @@ -592,6 +593,7 @@ test.describe('Copy/Cut/Paste Actions', () => {
);
await copyAndPaste(page);
await page.mouse.click(x, y);
await moveMouseAway(page);
});

test('Cut and Paste reaction with changed arrow and edit', async ({
Expand Down Expand Up @@ -688,6 +690,7 @@ test.describe('Copy/Cut/Paste Actions', () => {
await openFileAndAddToCanvas('Rxn-V2000/mapped-structure.rxn', page);
await copyAndPaste(page);
await page.mouse.click(x, y);
await moveMouseAway(page);
});

test('Cut and Paste Mapped reaction and edit', async ({ page }) => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion ketcher-autotests/tests/utils/canvas/selectSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TestIdSelectors } from '@utils/selectors/testIdSelectors';
import { getControlModifier } from '@utils/keyboard';
import { clickInTheMiddleOfTheScreen } from '@utils/clicks';
import { INPUT_DELAY } from '@utils/globals';
import { waitForRender } from '..';
import { moveMouseAway, waitForRender } from '..';

export enum SelectionType {
Rectangle = 'Rectangle',
Expand Down Expand Up @@ -94,6 +94,7 @@ export async function copyAndPaste(page: Page) {
await page.getByTestId('select-rectangle').first().click();
// to focus in Editor
await clickInTheMiddleOfTheScreen(page);
await moveMouseAway(page);
await waitForRender(page, async () => {
await page.keyboard.press(`${modifier}+KeyA`, { delay: INPUT_DELAY });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
BondDelete,
CalcImplicitH,
ImageDelete,
MultitailArrowDelete,
RGroupAttachmentPointRemove,
RxnArrowDelete,
RxnPlusDelete,
Expand All @@ -40,7 +41,7 @@ import { fromFragmentSplit } from './fragment';
import { fromRGroupAttachmentPointDeletion } from './rgroupAttachmentPoint';
import { ReStruct } from 'application/render';
import { isNumber } from 'lodash';
import { IMAGE_KEY } from 'domain/constants';
import { IMAGE_KEY, MULTITAIL_ARROW_KEY } from 'domain/constants';

export function fromOneAtomDeletion(restruct, atomId: number) {
return fromFragmentDeletion(restruct, { atoms: [atomId] });
Expand Down Expand Up @@ -222,6 +223,10 @@ export function fromFragmentDeletion(restruct, rawSelection) {
action.addOp(new ImageDelete(id));
});

selection[MULTITAIL_ARROW_KEY].forEach((id) => {
action.addOp(new MultitailArrowDelete(id));
});

const actionToDeleteRGroupAttachmentPoints = new Action();
selection.rgroupAttachmentPoints.forEach((id) => {
if (!removedRGroupAttachmentPoints.includes(id)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ import {
SimpleObjectMove,
TextMove,
ImageMove,
MultitailArrowMove,
} from '../operations';
import { Pile, RGroup, Vec2 } from 'domain/entities';
import { fromRGroupFragment, fromUpdateIfThen } from './rgroup';

import { Action } from './action';
import { fromAtomsFragmentAttr } from './atom';
import { getRelSGroupsBySelection } from './utils';
import { IMAGE_KEY } from 'domain/constants';
import { IMAGE_KEY, MULTITAIL_ARROW_KEY } from 'domain/constants';

export function fromMultipleMove(restruct, lists, d: Vec2) {
d = new Vec2(d);
Expand Down Expand Up @@ -135,6 +136,12 @@ export function fromMultipleMove(restruct, lists, d: Vec2) {
});
}

if (lists[MULTITAIL_ARROW_KEY]) {
lists[MULTITAIL_ARROW_KEY].forEach((multitailArrow) => {
action.addOp(new MultitailArrowMove(multitailArrow, d));
});
}

return action.perform(restruct);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './erase';
export * from './fragment';
export * from './paste';
export * from './image';
export * from './multitailArrow';
export * from './reaction';
export * from './rgroup';
export * from './rgroupAttachmentPoint';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { MultitailArrowReferencePosition, ReStruct } from 'application/render';
import {
Action,
MultitailArrowDelete,
MultitailArrowUpsert,
MultitailArrowMove,
MultitailArrowAddTail,
MultitailArrowRemoveTail,
MultitailArrowResizeTailHead,
MultitailArrowMoveHeadTail,
} from 'application/editor';
import { Vec2, MultitailArrow } from 'domain/entities';

export function fromMultitailArrowCreation(
reStruct: ReStruct,
topLeft: Vec2,
bottomRight: Vec2,
) {
const action = new Action();
const multitailArrow = MultitailArrow.fromTwoPoints(topLeft, bottomRight);
action.addOp(new MultitailArrowUpsert(multitailArrow));
return action.perform(reStruct);
}

export function fromMultitailArrowDeletion(reStruct: ReStruct, id: number) {
const action = new Action();
action.addOp(new MultitailArrowDelete(id));
return action.perform(reStruct);
}

export function fromMultitailArrowMove(
reStruct: ReStruct,
id: number,
offset: Vec2,
) {
const action = new Action();

action.addOp(new MultitailArrowMove(id, offset));
return action.perform(reStruct);
}

export function fromMultitailArrowTailAdd(reStruct: ReStruct, id: number) {
const action = new Action();

action.addOp(new MultitailArrowAddTail(id));
return action.perform(reStruct);
}

export function fromMultitailArrowTailRemove(
reStruct: ReStruct,
id: number,
tailId: number,
) {
const action = new Action();

action.addOp(new MultitailArrowRemoveTail(id, tailId));
return action.perform(reStruct);
}

export function fromMultitailArrowHeadTailsResize(
reStruct: ReStruct,
id: number,
ref: MultitailArrowReferencePosition,
offset: number,
) {
const action = new Action();
action.addOp(
new MultitailArrowResizeTailHead(id, offset, ref.name === 'head'),
);
return action.perform(reStruct);
}

export function fromMultitailArrowHeadTailMove(
reStruct: ReStruct,
id: number,
ref: MultitailArrowReferencePosition,
offset: number,
normalize?: true,
) {
const action = new Action();
action.addOp(
new MultitailArrowMoveHeadTail(id, offset, ref.name, ref.tailId, normalize),
);
return action.perform(reStruct);
}
14 changes: 13 additions & 1 deletion packages/ketcher-core/src/application/editor/actions/paste.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ import {
BondAttr,
AtomAttr,
ImageUpsert,
MultitailArrowUpsert,
} from '../operations';
import { fromRGroupAttrs, fromUpdateIfThen } from './rgroup';

import { Action } from './action';
import { SGroup, Struct, Vec2 } from 'domain/entities';
import { MultitailArrow, SGroup, Struct, Vec2 } from 'domain/entities';
import { fromSgroupAddition } from './sgroup';
import { fromRGroupAttachmentPointAddition } from './rgroupAttachmentPoint';
import { MonomerMicromolecule } from 'domain/entities/monomerMicromolecule';
Expand Down Expand Up @@ -197,6 +198,14 @@ export function fromPaste(
action.addOp(new ImageUpsert(clonedImage).perform(restruct));
});

pstruct.multitailArrows.forEach((multitailArrow: MultitailArrow) => {
const clonedMultitailArrow = multitailArrow.clone();
clonedMultitailArrow.move(offset);
action.addOp(
new MultitailArrowUpsert(clonedMultitailArrow).perform(restruct),
);
});

pstruct.rgroups.forEach((rg, rgid) => {
rg.frags.forEach((__frag, frid) => {
action.addOp(
Expand Down Expand Up @@ -248,6 +257,9 @@ function getStructCenter(struct: Struct): Vec2 {
if (struct.texts.size > 0) return struct.texts.get(0)!.position;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (struct.images.size > 0) return struct.images.get(0)!.center();
if (struct.multitailArrows.size > 0)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return struct.multitailArrows.get(0)!.center();

return new Vec2(0, 0);
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ export const OperationType = Object.freeze({
IMAGE_DELETE: 'Delete image',
IMAGE_MOVE: 'Move image',
IMAGE_RESIZE: 'Resize image',
MULTITAIL_ARROW_UPSERT: 'Upsert multitail arrow',
MULTITAIL_ARROW_DELETE: 'Delete multitail arrow',
MULTITAIL_ARROW_MOVE: 'Move multitail arrow',
MULTITAIL_ARROW_ADD_TAIL: 'Add multitail arrow tail',
MULTITAIL_ARROW_REMOVE_TAIL: 'Remove multitail arrow tail',
MULTITAIL_ARROW_RESIZE_HEAD_TAIL: 'Resize head tail',
MULTITAIL_ARROW_MOVE_HEAD_TAIL: 'Move head tail',
});

export enum OperationPriority {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export * from './calcimplicitH';
export * from './LoopMove';
export * from './OperationType';
export * from './image';
export * from './multitailArrow';
export * from './rgroup';
export * from './rgroupAttachmentPoint';
export * from './rxn';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './multitailArrowAddRemoveTail';
export * from './multitailArrowMove';
export * from './multitailArrowMoveHeadTail';
export * from './multitailArrowResizeTailHead';
export * from './multitailArrowUpsertDelete';
Loading

0 comments on commit e2aa8de

Please sign in to comment.