Skip to content

Commit

Permalink
#3129 - Full template preview following mouse cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
KonstantinEpam23 committed Sep 5, 2023
1 parent 8c0ba91 commit 62d6aca
Show file tree
Hide file tree
Showing 63 changed files with 191 additions and 23 deletions.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 @@ -83,4 +83,31 @@ test.describe('Preview for abbreviated structures: functional groups', () => {
await delay(DELAY_IN_SECONDS.ONE);
await takeEditorScreenshot(page);
});

test('Should show a preview following the mouse cursor', async ({ page }) => {
const bondId = 2;
await selectRingButton(RingButton.Benzene, page);
const bondPosition = await getBondByIndex(
page,
{ type: BondType.SINGLE },
bondId,
);
const pointAwayFromBond = { x: bondPosition.x + 100, y: bondPosition.y + 100 };
await page.mouse.move(pointAwayFromBond.x, pointAwayFromBond.y);
await takeEditorScreenshot(page);
});

test('Should show a preview following the mouse cursor and hide it when a bond is hovered over', async ({ page }) => {
const bondId = 2;
await selectRingButton(RingButton.Benzene, page);
const bondPosition = await getBondByIndex(
page,
{ type: BondType.SINGLE },
bondId,
);
const pointAwayFromBond = { x: bondPosition.x + 100, y: bondPosition.y + 100 };
await takeEditorScreenshot(page);
await page.mouse.move(pointAwayFromBond.x, pointAwayFromBond.y);
await takeEditorScreenshot(page);
});
});
2 changes: 2 additions & 0 deletions packages/ketcher-core/src/application/editor/actions/paste.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
TextCreate,
CalcImplicitH,
FragmentSetProperties,
BondAttr,
} from '../operations';
import { fromRGroupAttrs, fromUpdateIfThen } from './rgroup';

Expand Down Expand Up @@ -114,6 +115,7 @@ export function fromPaste(
action.addOp(operation);

pasteItems.bonds.push(operation.data.bid);
new BondAttr(operation.data.bid, 'isPreview', true).perform(restruct);
});

pasteItems.atoms.forEach((aid) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ import { fromAromaticTemplateOnBond } from './aromaticFusing';
import { fromPaste } from './paste';
import utils from '../shared/utils';
import { fromSgroupAddition } from './sgroup';
import { ReStruct } from 'application/render';

const benzeneMoleculeName = 'Benzene';
const cyclopentadieneMoleculeName = 'Cyclopentadiene';
const benzeneDoubleBondIndexes = [1, 4];

export function fromTemplateOnCanvas(
restruct,
restruct: ReStruct,
template,
pos,
angle,
pos: Vec2,
angle = 0,
): [Action, { atoms: number[]; bonds: number[] }] {
const [action, pasteItems] = fromPaste(
restruct,
Expand Down Expand Up @@ -153,6 +154,7 @@ export function fromTemplateOnAtom(
map.set(id, operation.data.aid);
pasteItems.atoms.push(operation.data.aid);
}
// new AtomAttr(id, 'isPreview', isPreview).perform(restruct);
});

if (!isTmplSingleGroup) mergeSgroups(action, restruct, pasteItems.atoms, aid);
Expand Down
3 changes: 2 additions & 1 deletion packages/ketcher-core/src/application/editor/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ function setFracAngle(angle: number): void {
FRAC = (Math.PI / 180) * angle;
}

function calcAngle(pos0: Vec2, pos1: Vec2): number {
function calcAngle(pos0: Vec2 | null, pos1: Vec2 | null): number {
if (!pos0 || !pos1) return 0;
const v = Vec2.diff(pos1, pos0);
return Math.atan2(v.y, v.x);
}
Expand Down
6 changes: 5 additions & 1 deletion packages/ketcher-core/src/domain/entities/vec2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ export class Vec2 {
}

sub(v: Vec2): Vec2 {
return new Vec2(this.x - v.x, this.y - v.y, this.z - v.z);
return new Vec2(
Number((this.x - v.x).toFixed(8)),
Number((this.y - v.y).toFixed(8)),
this.z - v.z,
);
}

scaled(s: number): Vec2 {
Expand Down
19 changes: 13 additions & 6 deletions packages/ketcher-react/src/script/editor/shared/closest.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,11 @@ function findClosestBond(restruct, pos, skip, minDist, options) {

let minCDist = minDist;

restruct.bonds.forEach((bond, bid) => {
if (bid === skipId) return;
restruct.bonds.forEach((bond, bondId) => {
if (bond.b.isPreview && !bond.hover) {
return;
}
if (bondId === skipId) return;

const p1 = restruct.atoms.get(bond.b.begin).a.pp;
const p2 = restruct.atoms.get(bond.b.end).a.pp;
Expand All @@ -208,7 +211,7 @@ function findClosestBond(restruct, pos, skip, minDist, options) {

if (isPosInsidePolygon) {
minCDist = cdist;
closestBondCenter = bid;
closestBondCenter = bondId;
}

const hb = restruct.molecule.halfBonds.get(bond.b.hb1);
Expand All @@ -222,7 +225,7 @@ function findClosestBond(restruct, pos, skip, minDist, options) {
const dist = Math.abs(Vec2.dot(pos.sub(p1), norm));

if (dist < minDist) {
closestBond = bid;
closestBond = bondId;
minDist = dist;
}
}
Expand Down Expand Up @@ -542,13 +545,17 @@ function findCloseMerge(
};

const struct = restruct.molecule;

selected.atoms.forEach((aid) => {
pos.atoms.set(aid, struct.atoms.get(aid).pp);
if (struct.atoms.get(aid)) {
pos.atoms.set(aid, struct.atoms.get(aid).pp);
}
});

selected.bonds.forEach((bid) => {
const bond = struct.bonds.get(bid);
if (!struct.bonds.get(bid)) {
return;
}
pos.bonds.set(
bid,
Vec2.lc2(
Expand Down
149 changes: 137 additions & 12 deletions packages/ketcher-react/src/script/editor/tool/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,97 @@ import {
Action,
vectorUtils,
Bond,
fromMultipleMove,
BondAttr,
ReBond,
} from 'ketcher-core';
import Editor from '../Editor';
import { getGroupIdsFromItemArrays } from './helper/getGroupIdsFromItems';
import { MODES } from 'src/constants';
import { Tool } from './Tool';

export const PREVIEW_DELAY = 300;
export const PREVIEW_DELAY = 200;

export function dropAndMerge(
editor: Editor,
mergeItems: any,
action?: Action,
resizeCanvas?: boolean,
): Action {
const restruct = editor.render.ctab;
const isMerging = !!mergeItems;
let dropItemAction = new Action();

if (isMerging) {
if (mergeItems.atomToFunctionalGroup) {
const [newMergeItems, extractAttachmentAtomAction] =
extractAttachmentAtom(mergeItems, editor);
mergeItems = newMergeItems;
dropItemAction = dropItemAction.mergeWith(extractAttachmentAtomAction);
}
dropItemAction = fromItemsFuse(restruct, mergeItems).mergeWith(
dropItemAction,
);
}

if (action) {
dropItemAction = dropItemAction.mergeWith(action);
}

const bonds = editor.selection()?.bonds ?? [];
for (const bondId of bonds) {
const rebond = restruct.bonds.get(bondId);
if (rebond) {
ReBond.bondRecalc(rebond, restruct, editor.options());
}
}

editor.hover(null);
if (isMerging) editor.selection(null);

if (dropItemAction?.operations.length > 0) {
editor.update(dropItemAction, false, { resizeCanvas: !!resizeCanvas });
}

return dropItemAction;
}

function extractAttachmentAtom(mergeItems, editor: Editor) {
const struct = editor.struct();
const reStruct = editor.render.ctab;

const newMergeItems = {
atoms: new Map(mergeItems.atoms),
bonds: new Map(mergeItems.bonds),
};

const action = new Action();

mergeItems.atomToFunctionalGroup?.forEach((functionalGroupId, srcAtomId) => {
const sGroup = struct.sgroups.get(functionalGroupId) as SGroup;

const { atomId: positionAtomId } = sGroup.getContractedPosition(
reStruct.molecule,
);

if (positionAtomId !== undefined) {
const atomsToDelete = [...SGroup.getAtoms(struct, sGroup)].filter(
(atomId) => atomId !== positionAtomId,
);
const bondsToDelete = [...SGroup.getBonds(struct, sGroup)];
action.mergeWith(fromSgroupDeletion(reStruct, functionalGroupId));
action.mergeWith(
fromFragmentDeletion(reStruct, {
atoms: atomsToDelete,
bonds: bondsToDelete,
}),
);
newMergeItems.atoms.set(srcAtomId, positionAtomId);
}
});

return [newMergeItems, action] as const;
}

function getBondFlipSign(struct: Struct, bond: Bond): number {
const xy0 = new Vec2();
Expand Down Expand Up @@ -122,12 +206,24 @@ class TemplateTool implements Tool {
private readonly isSaltOrSolvent: boolean;
private event: Event | undefined;

private templateSet: boolean;
private pasteItems: any;
private eventPos: Vec2;
private prevEventPos: Vec2;
private createPreviewAction: any;

constructor(editor: Editor, tmpl) {
this.editor = editor;
this.isPreviewVisible = false;
this.previewRemoveAction = new Action();
this.previewTimeout = null;
this.lastPreviewId = null;

this.templateSet = false;
this.pasteItems = null;
this.eventPos = new Vec2();
this.prevEventPos = new Vec2();

this.mode = getTemplateMode(tmpl);
this.editor.selection(null);
this.isSaltOrSolvent = SGroup.isSaltOrSolvent(tmpl.struct.name);
Expand Down Expand Up @@ -301,10 +397,7 @@ class TemplateTool implements Tool {
);
const restruct = this.editor.render.ctab;
const ci = this.editor.findItem(event, ['atoms', 'bonds']);

if (!ci) {
this.editor.hoverIcon.show();
}
this.eventPos = this.editor.render.page2obj(event);

const isMouseAwayFromAtomsAndBonds = !ci;
const isPreviewTargetChanged =
Expand All @@ -313,9 +406,6 @@ class TemplateTool implements Tool {
isMouseAwayFromAtomsAndBonds || isPreviewTargetChanged;
if (shouldHidePreview) {
this.hidePreview();
if (!this.editor.hoverIcon.isShown) {
this.editor.hoverIcon.show();
}
}

const shouldShowPreview =
Expand All @@ -328,10 +418,35 @@ class TemplateTool implements Tool {
this.lastPreviewId = getUniqueCiId(ci);

this.previewTimeout = setTimeout(() => {
if (this.createPreviewAction) {
const test = this.createPreviewAction.perform(restruct);
this.editor.update(test, false);
this.templateSet = false;
}
this.showPreview(event, ci, restruct);
}, PREVIEW_DELAY);
} else {
const mergeItems = getItemsToFuse(this.editor, this.pasteItems);
let action: any = null;
if (!this.templateSet && shouldHidePreview && !shouldShowPreview) {
[action, this.pasteItems] = fromTemplateOnCanvas(
restruct,
this.template,
this.editor.render.page2obj(event),
);
this.prevEventPos = this.editor.render.page2obj(event);
this.createPreviewAction = action;

dropAndMerge(this.editor, mergeItems, action, false);
this.editor.render.update(action, null, { resizeCanvas: false });
this.templateSet = true;
} else if (this.pasteItems && shouldHidePreview) {
const dist = this.eventPos.sub(this.prevEventPos);
this.prevEventPos = this.eventPos;
action = fromMultipleMove(restruct, this.pasteItems, dist);
this.editor.render.update(action, null, { resizeCanvas: false });
}
}

return true;
}

Expand Down Expand Up @@ -557,16 +672,21 @@ class TemplateTool implements Tool {

ci = { map: 'atoms', id: sGroupPositionAtomId };
}

if (!dragCtx.action) {
if (!ci) {
// ci.type == 'Canvas'
[action, pasteItems] = fromTemplateOnCanvas(
const result = fromTemplateOnCanvas(
restruct,
this.template,
dragCtx.xy0,
0,
);
action = result[0];
const pasteItems = result[1];
for (const bid of pasteItems.bonds || []) {
new BondAttr(bid, 'isPreview', false).perform(
this.editor.render.ctab,
);
}
dragCtx.action = action;
} else if (ci.map === 'atoms') {
const degree = restruct.atoms.get(ci.id)?.a.neighbors.length;
Expand Down Expand Up @@ -637,6 +757,11 @@ class TemplateTool implements Tool {
}

cancel() {
if (this.createPreviewAction && this.templateSet) {
const test = this.createPreviewAction.perform(this.editor.render.ctab);
this.editor.update(test, false);
this.templateSet = false;
}
this.hidePreview();
this.mouseup();
}
Expand Down

0 comments on commit 62d6aca

Please sign in to comment.