diff --git a/ketcher-autotests/tests/Indigo-Tools/Layout/layout.spec.ts b/ketcher-autotests/tests/Indigo-Tools/Layout/layout.spec.ts new file mode 100644 index 0000000000..5688bc03e0 --- /dev/null +++ b/ketcher-autotests/tests/Indigo-Tools/Layout/layout.spec.ts @@ -0,0 +1,35 @@ +import { Page, test } from '@playwright/test'; +import { + selectTopPanelButton, + TopPanelButton, + takeEditorScreenshot, + openFile, + pressButton, + waitForLoad, + getCoordinatesOfTheMiddleOfTheScreen, +} from '@utils'; + +async function openFileWithShift(filename: string, page: Page) { + await selectTopPanelButton(TopPanelButton.Open, page); + await openFile(filename, page); + await waitForLoad(page, async () => { + await pressButton(page, 'Add to Canvas'); + }); + const { x, y } = await getCoordinatesOfTheMiddleOfTheScreen(page); + const shift = 150; + await page.mouse.click(x + shift, y + shift); +} + +test.describe('Indigo Tools - Layout', () => { + test.beforeEach(async ({ page }) => { + await page.goto(''); + }); + + test('Center molecule after layout', async ({ page }) => { + // Related Github issue: https://github.com/epam/ketcher/issues/2078 + const anyStructure = 'benzene-rings.mol'; + await openFileWithShift(anyStructure, page); + await selectTopPanelButton(TopPanelButton.Layout, page); + await takeEditorScreenshot(page); + }); +}); diff --git a/ketcher-autotests/tests/Indigo-Tools/Layout/layout.spec.ts-snapshots/Indigo-Tools---Layout-Center-molecule-after-layout-1-chromium-linux.png b/ketcher-autotests/tests/Indigo-Tools/Layout/layout.spec.ts-snapshots/Indigo-Tools---Layout-Center-molecule-after-layout-1-chromium-linux.png new file mode 100644 index 0000000000..a7de561653 Binary files /dev/null and b/ketcher-autotests/tests/Indigo-Tools/Layout/layout.spec.ts-snapshots/Indigo-Tools---Layout-Center-molecule-after-layout-1-chromium-linux.png differ diff --git a/ketcher-autotests/tests/test-data/benzene-rings.mol b/ketcher-autotests/tests/test-data/benzene-rings.mol new file mode 100644 index 0000000000..6847f40470 --- /dev/null +++ b/ketcher-autotests/tests/test-data/benzene-rings.mol @@ -0,0 +1,43 @@ +null + Ketcher 7172314 12D 1 1.00000 0.00000 0 + + 18 20 0 0 0 0 0 0 0 0999 V2000 + 20.5750 -8.6000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.7090 -10.1000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.7090 -9.1000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.5750 -10.6000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.4410 -9.1000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.4410 -10.1000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.8679 -7.8929 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.1961 -7.4466 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.9014 -8.1523 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.4545 -6.4796 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.1273 -6.9220 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.4156 -6.2194 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.1227 -5.5123 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.8981 -4.2900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.1560 -5.2537 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.6054 -3.5817 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.8329 -4.8013 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.5678 -3.8369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3 1 2 0 0 0 + 4 2 2 0 0 0 + 1 5 1 0 0 0 + 2 3 1 0 0 0 + 5 6 2 0 0 0 + 6 4 1 0 0 0 + 1 7 1 0 0 0 + 9 7 2 0 0 0 + 10 8 2 0 0 0 + 7 11 1 0 0 0 + 8 9 1 0 0 0 + 11 12 2 0 0 0 + 12 10 1 0 0 0 + 12 13 1 0 0 0 + 15 13 2 0 0 0 + 16 14 2 0 0 0 + 13 17 1 0 0 0 + 14 15 1 0 0 0 + 17 18 2 0 0 0 + 18 16 1 0 0 0 +M END diff --git a/packages/ketcher-react/src/script/editor/Editor.ts b/packages/ketcher-react/src/script/editor/Editor.ts index 142641d4a3..775057046d 100644 --- a/packages/ketcher-react/src/script/editor/Editor.ts +++ b/packages/ketcher-react/src/script/editor/Editor.ts @@ -25,7 +25,6 @@ import { fromDescriptorsAlign, fromMultipleMove, fromNewCanvas, - ReStruct, } from 'ketcher-core'; import { DOMSubscription, @@ -47,6 +46,11 @@ import { ToolConstructorInterface, ToolEventHandlerName, } from './tool/Tool'; +import { + getSelectionMap, + getStructCenter, + recoordinate, +} from './utils/structLayout'; const SCALE = 40; const HISTORY_SIZE = 32; // put me to options @@ -83,7 +87,7 @@ const highlightTargets = [ function selectStereoFlagsIfNecessary( atoms: any, - expAtoms: number[] + expAtoms: number[], ): number[] { const atomsOfFragments = {}; atoms.forEach((atom, atomId) => { @@ -157,8 +161,8 @@ class Editor implements KetcherEditor { { scale: SCALE, }, - options - ) + options, + ), ); this._selection = null; // eslint-disable-line @@ -304,7 +308,7 @@ class Editor implements KetcherEditor { this.render = new Render( this.render.clientArea, - Object.assign({ scale: SCALE }, value) + Object.assign({ scale: SCALE }, value), ); this.struct(struct); this.render.setZoom(zoom); @@ -332,15 +336,15 @@ class Editor implements KetcherEditor { const structure = this.render.ctab; const { scale, offset } = this.render.options; const structCenter = getStructCenter(structure); - const canvasCenter = this.render.sz.scaled(1 / scale).scaled(0.5); + const { width, height } = this.render.clientArea.getBoundingClientRect(); + const canvasCenterVector = new Vec2(width, height); + const canvasCenter = this.render.view2obj(canvasCenterVector).scaled(0.5); + const shiftFactor = 0.4; const shiftVector = canvasCenter .sub(structCenter) - .sub(offset.scaled(1 / scale)); + .sub(offset.scaled(shiftFactor / scale)); - const structureToMove = Object.keys(ReStruct.maps).reduce((result, map) => { - result[map] = Array.from(structure[map].keys()); - return result; - }, {}); + const structureToMove = getSelectionMap(structure); const action = fromMultipleMove(structure, structureToMove, shiftVector); this.update(action, true); @@ -365,7 +369,7 @@ class Editor implements KetcherEditor { this.zoom( newZoomValue < MIN_ZOOM_VALUE ? MIN_ZOOM_VALUE - : Number(newZoomValue.toFixed(2)) + : Number(newZoomValue.toFixed(2)), ); } } @@ -406,12 +410,12 @@ class Editor implements KetcherEditor { } const stereoFlags = selectStereoFlagsIfNecessary( this.struct().atoms, - this.explicitSelected().atoms + this.explicitSelected().atoms, ); if (stereoFlags.length !== 0) { this._selection && this._selection.enhancedFlags ? (this._selection.enhancedFlags = Array.from( - new Set([...this._selection.enhancedFlags, ...stereoFlags]) + new Set([...this._selection.enhancedFlags, ...stereoFlags]), )) : (res.enhancedFlags = stereoFlags); } @@ -455,7 +459,7 @@ class Editor implements KetcherEditor { update( action: Action | true, ignoreHistory?: boolean, - options = { resizeCanvas: true } + options = { resizeCanvas: true }, ) { setFunctionalGroupsTooltip({ editor: this, @@ -616,7 +620,7 @@ class Editor implements KetcherEditor { true, null, new Pile(selection.simpleObjects), - new Pile(selection.texts) + new Pile(selection.texts), ); // Copy by its own as Struct.clone doesn't support @@ -655,7 +659,7 @@ function resetSelectionOnCanvasClick( editor: Editor, eventName: string, clientArea: HTMLElement, - event + event, ) { if ( eventName === 'mouseup' && @@ -687,7 +691,7 @@ function useToolIfNeeded( editor: Editor, eventHandlerName: ToolEventHandlerName, clientArea: HTMLElement, - event + event, ) { const editorTool = editor.tool(); if (!editorTool) { @@ -787,7 +791,7 @@ function domEventSetup(editor: Editor, clientArea: HTMLElement) { editor, toolEventHandler, clientArea, - event + event, ); if (isToolUsed) { return true; @@ -800,23 +804,6 @@ function domEventSetup(editor: Editor, clientArea: HTMLElement) { }); } -function recoordinate(editor: Editor, rp?: Vec2 /* , vp */) { - // rp is a point in scaled coordinates, which will be positioned - // vp is the point where the reference point should now be (in view coordinates) - // or the center if not set - console.assert(rp, 'Reference point not specified'); - if (rp) { - editor.render.setScrollOffset(rp.x, rp.y); - } else { - editor.render.setScrollOffset(0, 0); - } -} - -function getStructCenter(ReStruct, selection?) { - const bb = ReStruct.getVBoxObj(selection || {}); - return Vec2.lc2(bb.p0, 0.5, bb.p1, 0.5); -} - export { Editor }; export default Editor; diff --git a/packages/ketcher-react/src/script/editor/utils/index.ts b/packages/ketcher-react/src/script/editor/utils/index.ts index 52b844087f..51c4e6a82a 100644 --- a/packages/ketcher-react/src/script/editor/utils/index.ts +++ b/packages/ketcher-react/src/script/editor/utils/index.ts @@ -16,3 +16,4 @@ export * from './customOnChangeHandler'; export * from './elementOffset'; +export * from './structLayout'; diff --git a/packages/ketcher-react/src/script/editor/utils/structLayout.ts b/packages/ketcher-react/src/script/editor/utils/structLayout.ts new file mode 100644 index 0000000000..d7db2446ef --- /dev/null +++ b/packages/ketcher-react/src/script/editor/utils/structLayout.ts @@ -0,0 +1,23 @@ +import { Editor, ReStruct, Vec2 } from 'ketcher-core'; + +export function getSelectionMap(structure: ReStruct) { + return Object.keys(ReStruct.maps).reduce((result, map) => { + result[map] = Array.from(structure[map].keys()); + return result; + }, {}); +} + +export function getStructCenter(ReStruct, selection?) { + const bb = ReStruct.getVBoxObj(selection || {}); + return Vec2.lc2(bb.p0, 0.5, bb.p1, 0.5); +} + +export function recoordinate(editor: Editor, rp?: Vec2) { + // rp is a point in scaled coordinates, which will be positioned + console.assert(rp, 'Reference point not specified'); + if (rp) { + editor.render.setScrollOffset(rp.x, rp.y); + } else { + editor.render.setScrollOffset(0, 0); + } +}