Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2742 [Part 2] Allow to hover on or select R-Group attachment points #2954

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -4,7 +4,7 @@ import {
RGroupAttachmentPointRemove,
} from '../operations';
import { Action } from './action';
import { RGroupAttachmentPoint, Struct } from 'domain/entities';
import { Struct } from 'domain/entities';

function fromRGroupAttachmentPointUpdate(
restruct: ReStruct,
Expand Down Expand Up @@ -64,9 +64,9 @@ function fromRGroupAttachmentPointAddition(attpnt, atomId: number) {
function fromRGroupAttachmentPointsDeletion(struct: Struct, atomId: number) {
const action = new Action();
const attachmentPointsToDelete =
RGroupAttachmentPoint.getRGroupAttachmentPointsByAtomId(atomId, struct);
attachmentPointsToDelete.forEach((_attachmentPoint, id) => {
SashaGraves marked this conversation as resolved.
Show resolved Hide resolved
action.addOp(new RGroupAttachmentPointRemove(id));
struct.getRGroupAttachmentPointsByAtomId(atomId);
attachmentPointsToDelete.forEach((rgroupAttachmentPointId) => {
action.addOp(new RGroupAttachmentPointRemove(rgroupAttachmentPointId));
});
return action;
}
Expand Down
27 changes: 15 additions & 12 deletions packages/ketcher-core/src/application/render/restruct/rergroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import ReObject from './reobject';
import { Scale } from 'domain/helpers';
import draw from '../draw';
import util from '../util';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Render } from '../raphaelRender';

const BORDER_EXT = new Vec2(0.05 * 3, 0.05 * 3);
const PADDING_VECTOR = new Vec2(0.2, 0.4);
Expand Down Expand Up @@ -55,7 +57,11 @@ class ReRGroup extends ReObject {
return ret;
}

/**
* @param {Render} render
*/
calcBBox(render) {
/** @type {Box2Abs | null} */
let rGroupBoundingBox = null;
this.item.frags.forEach((fid) => {
const fragBox = render.ctab.frags
Expand All @@ -68,22 +74,19 @@ class ReRGroup extends ReObject {
}
});

const rGroupAttachmentPointsVBox =
render.ctab.getRGroupAttachmentPointsVBoxByAtomIds(this.getAtoms(render));
if (rGroupBoundingBox && rGroupAttachmentPointsVBox) {
rGroupBoundingBox = Box2Abs.union(
rGroupBoundingBox,
rGroupAttachmentPointsVBox,
);
}

rGroupBoundingBox = rGroupBoundingBox
? rGroupBoundingBox.extend(BORDER_EXT, BORDER_EXT)
: rGroupBoundingBox;

let attachmentPointsVBox = render.ctab.getAttachmentsPointsVBox(
this.getAtoms(render),
);
attachmentPointsVBox = attachmentPointsVBox
? attachmentPointsVBox.extend(BORDER_EXT, BORDER_EXT)
: attachmentPointsVBox;

rGroupBoundingBox =
attachmentPointsVBox && rGroupBoundingBox
? Box2Abs.union(attachmentPointsVBox, rGroupBoundingBox)
: rGroupBoundingBox;

return rGroupBoundingBox;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RaphaelPaper } from 'raphael';
import {
Bond,
Box2Abs,
RGroupAttachmentPoint,
RGroupAttachmentPointType,
Struct,
Expand All @@ -10,45 +10,141 @@ import { Scale } from 'domain/helpers';
import { ReAtom, ReObject, ReStruct } from '.';
import draw from '../draw';
import { Render } from '../raphaelRender';
import { RenderOptions } from '../render.types';
import { LayerMap } from './generalEnumTypes';
import Visel from './visel';

class ReRGroupAttachmentPoint extends ReObject {
item: RGroupAttachmentPoint;
reAtom: ReAtom;
lineDirectionVector: Vec2 = new Vec2();

static LINE_OUTLINE_WIDTH = 0.36;
static OUTLINE_PADDING = 0.15;
static CURVE_OUTLINE_WIDTH = 1.0;
static CURVE_OUTLINE_HEIGHT = 0.42;

constructor(item: RGroupAttachmentPoint, reAtom: ReAtom) {
super('rgroupAttachmentPoint');
this.item = item;
this.reAtom = reAtom;
}

get normalizedLineDirectionVector() {
return this.lineDirectionVector.normalized();
}

get normalizedCurveDirectionVector() {
return this.lineDirectionVector.rotate(Math.PI / 2).normalized();
}

get startPoint() {
return this.reAtom.a.pp;
}

get middlePoint() {
return this.outlineEndPoint.addScaled(
this.normalizedLineDirectionVector,
-ReRGroupAttachmentPoint.CURVE_OUTLINE_HEIGHT,
);
}

get endPoint() {
return this.startPoint.add(this.lineDirectionVector);
}

get outlineEndPoint() {
const length =
this.lineDirectionVector.length() +
ReRGroupAttachmentPoint.OUTLINE_PADDING;
return this.startPoint.addScaled(
this.normalizedLineDirectionVector,
length,
);
}

static isSelectable() {
return false;
return true;
}

/**
* Why?
* We need to return Bounding box for the attachment points for this atom,
* to be able to correctly calculate boundaries for autoscaling and positioning
*/
getVBoxObj(render: Render): Box2Abs | null {
let accumulatedBBox: Box2Abs | null = null;
const directionVector = this.getAttachmentPointDirectionVector(
render.ctab.molecule,
getOutlinePoints() {
const topLeftPadPoint = this.outlineEndPoint.addScaled(
this.normalizedCurveDirectionVector,
-ReRGroupAttachmentPoint.CURVE_OUTLINE_WIDTH / 2,
);
if (!directionVector) {
return null;
}
const attachmentPointEndPosition = this.reAtom.a.pp.add(directionVector);
const attachmentPointEndBoundingBox = new Box2Abs(
attachmentPointEndPosition,
attachmentPointEndPosition,
const topLeftPoint = topLeftPadPoint.addScaled(
this.normalizedCurveDirectionVector,
ReRGroupAttachmentPoint.OUTLINE_PADDING,
);
const topRightPadPoint = this.outlineEndPoint.addScaled(
this.normalizedCurveDirectionVector,
ReRGroupAttachmentPoint.CURVE_OUTLINE_WIDTH / 2,
);
const topRightPoint = topRightPadPoint.addScaled(
this.normalizedCurveDirectionVector,
-ReRGroupAttachmentPoint.OUTLINE_PADDING,
);
const middleMostLeftPadPoint = this.middlePoint.addScaled(
this.normalizedCurveDirectionVector,
-ReRGroupAttachmentPoint.CURVE_OUTLINE_WIDTH / 2,
);
const middleMostLeftPoint = middleMostLeftPadPoint.addScaled(
this.normalizedCurveDirectionVector,
ReRGroupAttachmentPoint.OUTLINE_PADDING,
);
const middleMostRightPadPoint = this.middlePoint.addScaled(
this.normalizedCurveDirectionVector,
ReRGroupAttachmentPoint.CURVE_OUTLINE_WIDTH / 2,
);
accumulatedBBox = accumulatedBBox
? Box2Abs.union(accumulatedBBox, attachmentPointEndBoundingBox)
: attachmentPointEndBoundingBox;
return accumulatedBBox;
const middleMostRightPoint = middleMostRightPadPoint.addScaled(
this.normalizedCurveDirectionVector,
-ReRGroupAttachmentPoint.OUTLINE_PADDING,
);
const middleLeftPoint = this.middlePoint.addScaled(
this.normalizedCurveDirectionVector,
-ReRGroupAttachmentPoint.LINE_OUTLINE_WIDTH / 2,
);
const middleRightPoint = this.middlePoint.addScaled(
this.normalizedCurveDirectionVector,
ReRGroupAttachmentPoint.LINE_OUTLINE_WIDTH / 2,
);
const bottomLeftPadPoint = this.startPoint.addScaled(
this.normalizedCurveDirectionVector,
-ReRGroupAttachmentPoint.LINE_OUTLINE_WIDTH / 2,
);
const bottomLeftPoint = bottomLeftPadPoint.addScaled(
this.normalizedLineDirectionVector,
ReRGroupAttachmentPoint.OUTLINE_PADDING,
);
const bottomRightPadPoint = this.startPoint.addScaled(
this.normalizedCurveDirectionVector,
ReRGroupAttachmentPoint.LINE_OUTLINE_WIDTH / 2,
);
const bottomRightPoint = bottomRightPadPoint.addScaled(
this.normalizedLineDirectionVector,
ReRGroupAttachmentPoint.OUTLINE_PADDING,
);

return [
topLeftPadPoint,
topLeftPoint,
topRightPoint,
topRightPadPoint,
middleMostRightPadPoint,
middleMostRightPoint,
middleRightPoint,
bottomRightPoint,
bottomRightPadPoint,
bottomLeftPadPoint,
bottomLeftPoint,
middleLeftPoint,
middleMostLeftPoint,
middleMostLeftPadPoint,
] as const;
}

getDistanceTo(destination: Vec2) {
return Vec2.dist(destination, this.middlePoint);
}

show(restruct: ReStruct) {
Expand All @@ -59,6 +155,7 @@ class ReRGroupAttachmentPoint extends ReObject {
if (!directionVector) {
return;
}
this.lineDirectionVector = directionVector;

showAttachmentPointShape(
this.reAtom,
Expand All @@ -83,6 +180,63 @@ class ReRGroupAttachmentPoint extends ReObject {
}
}

private getHoverPlatePath(options: RenderOptions) {
const outlinePoints = this.getOutlinePoints();
const scaledOutlinePoints = outlinePoints.map((point) =>
Scale.obj2scaled(point, options),
);
const [
topLeftPadPoint,
topLeftPoint,
topRightPoint,
topRightPadPoint,
middleMostRightPadPoint,
middleMostRightPoint,
middleRightPoint,
bottomRightPoint,
bottomRightPadPoint,
bottomLeftPadPoint,
bottomLeftPoint,
middleLeftPoint,
middleMostLeftPoint,
middleMostLeftPadPoint,
] = scaledOutlinePoints;

// Docs: ketcher-core/docs/data/hover_selection_rgroup_attachment_point.png
const pathString = `
M ${topLeftPoint.x} ${topLeftPoint.y}
L ${topRightPoint.x} ${topRightPoint.y}
C ${topRightPadPoint.x} ${topRightPadPoint.y}, ${middleMostRightPadPoint.x} ${middleMostRightPadPoint.y}, ${middleMostRightPoint.x} ${middleMostRightPoint.y}
L ${middleRightPoint.x} ${middleRightPoint.y}
L ${bottomRightPoint.x} ${bottomRightPoint.y}
C ${bottomRightPadPoint.x} ${bottomRightPadPoint.y}, ${bottomLeftPadPoint.x} ${bottomLeftPadPoint.y}, ${bottomLeftPoint.x} ${bottomLeftPoint.y}
L ${middleLeftPoint.x} ${middleLeftPoint.y}
L ${middleMostLeftPoint.x} ${middleMostLeftPoint.y}
C ${middleMostLeftPadPoint.x} ${middleMostLeftPadPoint.y}, ${topLeftPadPoint.x} ${topLeftPadPoint.y}, ${topLeftPoint.x} ${topLeftPoint.y}
`;
return pathString;
}

makeHoverPlate(render: Render) {
const hoverPlatePath = this.getHoverPlatePath(render.options);
return render.paper.path(hoverPlatePath).attr(render.options.hoverStyle);
}

makeSelectionPlate(
_restruct: ReStruct,
paper: RaphaelPaper,
options: RenderOptions,
) {
const hoverPlatePath = this.getHoverPlatePath(options);
return paper.path(hoverPlatePath).attr(options.selectionStyle);
}

drawHover(render: Render) {
const hoverPlate = this.makeHoverPlate(render);
render.ctab.addReObjectPath(LayerMap.hovering, this.visel, hoverPlate);
return hoverPlate;
}

private getAttachmentPointDirectionVector(struct: Struct) {
if (!this.reAtom.hasAttachmentPoint()) {
return;
Expand Down Expand Up @@ -133,7 +287,13 @@ function showAttachmentPointShape(
options,
);

addReObjectPath(LayerMap.indices, visel, resultShape, atomPositionVector);
addReObjectPath(
LayerMap.indices,
visel,
resultShape,
atomPositionVector,
true,
);
}

function trisectionLargestSector(
Expand Down Expand Up @@ -201,7 +361,7 @@ function showAttachmentPointLabel(
options,
atom.color,
);
addReObjectPath(LayerMap.indices, visel, labelPath, atomPositionVector);
addReObjectPath(LayerMap.indices, visel, labelPath, atomPositionVector, true);
}

function getLabelPositionForAttachmentPoint(
Expand Down
Loading