From 5fb5bcd46b77f87d8ed332962103a35fb3f8055a Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM <167520099+DmitriiP-EPAM@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:18:16 +0300 Subject: [PATCH 01/24] Add files via upload --- .../SnakeModeHydrogenBondRenderer.ts | 988 ++++++++++++++++++ 1 file changed, 988 insertions(+) create mode 100644 packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts new file mode 100644 index 0000000000..3ef890c57c --- /dev/null +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts @@ -0,0 +1,988 @@ +import { SnakeMode } from 'application/editor'; +import { editorEvents } from 'application/editor/editorEvents'; +import { CoreEditor } from 'application/editor/internal'; +import { Coordinates } from 'application/editor/shared/coordinates'; +import { + BaseMonomerRenderer, + BaseSequenceItemRenderer, +} from 'application/render'; +import { D3SvgElementSelection } from 'application/render/types'; +import assert from 'assert'; +import { BaseMonomer, Vec2 } from 'domain/entities'; +import { Cell } from 'domain/entities/canvas-matrix/Cell'; +import { + Connection, + ConnectionDirectionInDegrees, + ConnectionDirectionOfLastCell, +} from 'domain/entities/canvas-matrix/Connection'; +import { SNAKE_LAYOUT_CELL_WIDTH } from 'domain/entities/DrawingEntitiesManager'; +import { DrawingEntity } from 'domain/entities/DrawingEntity'; +import { PolymerBond } from 'domain/entities/PolymerBond'; +import { getSugarFromRnaBase } from 'domain/helpers/monomers'; +import { BaseRenderer } from '../BaseRenderer'; + +enum LineDirection { + Horizontal = 'Horizontal', + Vertical = 'Vertical', +} + +const LINE_FROM_MONOMER_LENGTH = 15; +const VERTICAL_LINE_LENGTH = 21; +const RNA_CHAIN_VERTICAL_LINE_LENGTH = 74; +const CORNER_LENGTH = 4; +const DOUBLE_CORNER_LENGTH = CORNER_LENGTH * 2; + +const BOND_END_LENGTH = 15; +const CELL_HEIGHT = 40; +const SMOOTH_CORNER_SIZE = 5; +const SIDE_CONNECTION_BODY_ELEMENT_CLASS = 'polymer-bond-body'; + +// TODO: Reorganize. Take `class SnakeModePolymerBondRenderer` into account. +export class SnakeModeHydrogenBondRenderer extends BaseRenderer { + private editorEvents: typeof editorEvents; + private isSnakeBond = false; // `SnakeModeBackboneBondRenderer` or `SnakeModeRNABaseAndSugarBondRenderer`. + // TODO: Specify the types. + private selectionElement; + private path = ''; + private previousStateOfIsMonomersOnSameHorizontalLine = false; + private sideConnectionBondTurnPoint?: number; + public declare bodyElement?: D3SvgElementSelection; + + constructor(public readonly polymerBond: PolymerBond) { + super(polymerBond as DrawingEntity); + this.polymerBond.setRenderer(this); + this.editorEvents = editorEvents; + this.calculateIsSnakeBond(); + } + + // TODO: Delete. + public get isSnake(): true { + return true; + } + + public get rootBBox(): DOMRect | undefined { + const rootNode = this.rootElement?.node(); + if (!rootNode) return; + + return rootNode.getBBox(); + } + + public get width(): number { + return this.rootBBox?.width || 0; + } + + public get height(): number { + return this.rootBBox?.height || 0; + } + + private get scaledPosition(): { + readonly endPosition: Vec2; + readonly startPosition: Vec2; + } { + // we need to convert monomer coordinates(stored in angstroms) to pixels. + // it needs to be done in view layer of application (like renderers) + const startPositionInPixels = Coordinates.modelToCanvas( + this.polymerBond.startPosition, + ); + + const endPositionInPixels = Coordinates.modelToCanvas( + this.polymerBond.endPosition, + ); + + return { + startPosition: startPositionInPixels, + endPosition: endPositionInPixels, + }; + } + + public moveSelection(): void { + if ( + this.previousStateOfIsMonomersOnSameHorizontalLine !== + this.isMonomersOnSameHorizontalLine() + ) { + this.remove(); + this.show(); + } else { + assert(this.rootElement); + this.moveStart(); + this.moveEnd(); + } + this.previousStateOfIsMonomersOnSameHorizontalLine = + this.isMonomersOnSameHorizontalLine(); + } + + // TODO: Specify the types. + public appendBond(rootElement) { + const editor = CoreEditor.provideEditorInstance(); + const matrix = editor.drawingEntitiesManager.canvasMatrix; + const cells = matrix?.polymerBondToCells.get(this.polymerBond); + + if ( + this.polymerBond.isSideChainConnection && + editor.mode instanceof SnakeMode && + cells + ) { + this.appendSideConnectionBond(rootElement, cells); + } else if ( + this.isSnakeBond && + this.polymerBond.finished && + !this.isMonomersOnSameHorizontalLine() + ) { + this.appendSnakeBond(rootElement); + } else { + this.appendBondGraph(rootElement); + } + + return this.bodyElement; + } + + // TODO: Specify the types. + public appendSnakeBond(rootElement) { + const startPosition = this.scaledPosition.startPosition; + const endPosition = this.scaledPosition.endPosition; + this.updateSnakeBondPath(startPosition, endPosition); + + this.bodyElement = rootElement + .append('path') + .attr('stroke', this.polymerBond.finished ? '#333333' : '#0097A8') + .attr('stroke-width', 1) + .attr('class', 'selection-area') + .attr('d', this.path) + .attr('fill-opacity', 0) + .attr('pointer-events', 'stroke'); + return this.bodyElement; + } + + private drawPartOfSideConnection( + isHorizontal: boolean, + connection: Connection, + cell: Cell, + direction: ConnectionDirectionInDegrees, + ): string { + const sin = Math.sin((direction * Math.PI) / 180); + const cos = Math.cos((direction * Math.PI) / 180); + const xOffset = (SNAKE_LAYOUT_CELL_WIDTH / 2) * cos; + const yOffset = (CELL_HEIGHT / 2) * sin; + const maxXOffset = cell.connections.reduce( + (max: number, connection: Connection): number => { + return max > connection.xOffset ? max : connection.xOffset; + }, + 0, + ); + const maxYOffset = cell.connections.reduce( + (max: number, connection: Connection): number => { + const connectionYOffset = connection.yOffset || 0; + return max > connectionYOffset ? max : connectionYOffset; + }, + 0, + ); + + let endOfPathPart: number; + if (isHorizontal && this.sideConnectionBondTurnPoint) { + endOfPathPart = this.sideConnectionBondTurnPoint; + } else { + const { monomerSize, scaledMonomerPosition } = ( + cell.monomer as BaseMonomer + ).renderer as BaseMonomerRenderer | BaseSequenceItemRenderer; + endOfPathPart = isHorizontal + ? scaledMonomerPosition.x + monomerSize.width / 2 + xOffset + : scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; + } + + this.sideConnectionBondTurnPoint = endOfPathPart; + + if (isHorizontal) { + endOfPathPart += + -(connection.yOffset || 0) * 3 + + cos * -connection.xOffset * 3 + + cos * (maxXOffset + 1) * 3 + + (maxYOffset + 1) * 3; + } + let pathPart = isHorizontal ? 'H ' : 'V '; + pathPart += `${endOfPathPart - SMOOTH_CORNER_SIZE * cos} `; + pathPart += generateBend(cos, sin, cos, 1); + + return pathPart; + } + + // TODO: Specify the types. + private appendSideConnectionBond(rootElement, cells: Cell[]) { + const firstCell = cells[0]; + const firstCellConnection = firstCell.connections.find( + (connection: Connection): boolean => { + return connection.polymerBond === this.polymerBond; + }, + ) as Connection; + const isVerticalConnection = firstCellConnection.isVertical; + const isStraightVerticalConnection = + cells.length === 2 && isVerticalConnection; + const isFirstMonomerOfBondInFirstCell = firstCell.node?.monomers.includes( + this.polymerBond.firstMonomer, + ); + const isTwoNeighborRowsConnection = cells.every( + (cell) => cell.y === firstCell.y || cell.y === firstCell.y + 1, + ); + const startPosition = isFirstMonomerOfBondInFirstCell + ? this.scaledPosition.startPosition + : this.scaledPosition.endPosition; + const endPosition = isFirstMonomerOfBondInFirstCell + ? this.scaledPosition.endPosition + : this.scaledPosition.startPosition; + const xDirection = + startPosition.x >= (this.sideConnectionBondTurnPoint || endPosition.x) + ? 180 + : 0; + let dAttributeForPath = `M ${startPosition.x},${startPosition.y} `; + + const cos = Math.cos((xDirection * Math.PI) / 180); + + let previousConnection: Connection; + let previousCell: Cell; + + const horizontalPartIntersectionsOffset = firstCellConnection.xOffset; + + const areCellsOnSameRow = cells.every((cell) => { + return cell.y === firstCell.y; + }); + const isSecondCellEmpty = cells[1].node === null; + + if (areCellsOnSameRow) { + dAttributeForPath += `L ${startPosition.x},${ + startPosition.y - + BOND_END_LENGTH - + horizontalPartIntersectionsOffset * 3 + } `; + dAttributeForPath += generateBend(0, -1, cos, -1); + } else { + dAttributeForPath += `L ${startPosition.x},${ + startPosition.y + + BOND_END_LENGTH + + horizontalPartIntersectionsOffset * 3 + } `; + if ( + !isStraightVerticalConnection && + !isSecondCellEmpty && + !isTwoNeighborRowsConnection + ) { + dAttributeForPath += generateBend(0, 1, cos, 1); + } + } + + if (isVerticalConnection && !isStraightVerticalConnection) { + dAttributeForPath += this.drawPartOfSideConnection( + true, + firstCellConnection, + firstCell, + this.sideConnectionBondTurnPoint && + startPosition.x < this.sideConnectionBondTurnPoint + ? 0 + : 180, + ); + } + + let maxHorizontalOffset = 0; + + cells.forEach((cell: Cell, cellIndex: number): void => { + const cellConnection = cell.connections.find( + (connection: Connection): boolean => { + return connection.polymerBond === this.polymerBond; + }, + ) as Connection; + const isLastCell = cellIndex === cells.length - 1; + const _xDirection = this.sideConnectionBondTurnPoint + ? endPosition.x < this.sideConnectionBondTurnPoint + ? 180 + : 0 + : xDirection; + const maxXOffset = cell.connections.reduce( + (max: number, connection: Connection): number => { + return connection.isVertical || max > connection.xOffset + ? max + : connection.xOffset; + }, + 0, + ); + + maxHorizontalOffset = + maxHorizontalOffset > maxXOffset ? maxHorizontalOffset : maxXOffset; + + if (isLastCell) { + if (isStraightVerticalConnection) { + return; + } + + const directionObject = + cellConnection.direction as ConnectionDirectionOfLastCell; + const yDirection = isVerticalConnection ? 90 : directionObject.y; + const sin = Math.sin((yDirection * Math.PI) / 180); + const cos = Math.cos((_xDirection * Math.PI) / 180); + + if (!areCellsOnSameRow) { + dAttributeForPath += `V ${ + endPosition.y - + CELL_HEIGHT / 2 - + SMOOTH_CORNER_SIZE - + sin * (cellConnection.yOffset || 0) * 3 - + (isTwoNeighborRowsConnection + ? maxHorizontalOffset - cellConnection.xOffset + : cellConnection.xOffset) * + 3 + } `; + dAttributeForPath += generateBend(0, sin, cos, 1); + } + dAttributeForPath += `H ${endPosition.x - SMOOTH_CORNER_SIZE * cos} `; + dAttributeForPath += generateBend(cos, 0, cos, 1); + return; + } + // empty cells + if (cell.node === null) { + return; + } + + // other cells + if ( + previousConnection && + previousConnection.direction !== cellConnection.direction + ) { + const isHorizontal = + previousConnection.direction === 0 || + previousConnection.direction === 180; + + dAttributeForPath += this.drawPartOfSideConnection( + isHorizontal, + previousConnection, + previousCell, + // FIXME: Check. Is it correct to use `as ConnectionDirectionInDegrees` here? + isHorizontal + ? xDirection + : (previousConnection.direction as ConnectionDirectionInDegrees), + ); + } + previousCell = cell; + previousConnection = cellConnection; + }); + + dAttributeForPath += `L ${endPosition.x},${endPosition.y} `; + + this.bodyElement = rootElement + .append('path') + .attr('class', `${SIDE_CONNECTION_BODY_ELEMENT_CLASS}`) + .attr('stroke', '#333333') + .attr('stroke-width', 1) + .attr('d', dAttributeForPath) + .attr('fill', 'none') + .attr('stroke-dasharray', '2') + .attr('pointer-events', 'all'); + + this.path = dAttributeForPath; + + return this.bodyElement; + } + + private getMonomerWidth(): number { + return this.polymerBond.firstMonomer.renderer?.monomerSize.width ?? 0; + } + + private getMonomerHeight(): number { + return this.polymerBond.firstMonomer.renderer?.monomerSize.height ?? 0; + } + + public isMonomersOnSameHorizontalLine(): boolean { + if (!this.polymerBond.secondMonomer) return false; + + const monomer1Y = this.polymerBond.firstMonomer.position.y; + const monomer2Y = this.polymerBond.secondMonomer.position.y; + const difference = monomer1Y - monomer2Y; + return difference < 0.5 && difference > -0.5; + } + + private updateSnakeBondPath( + startPosition: Vec2, + endPosition: Vec2, + reCheckAttachmentPoint = true, + ): void { + const isR1TheCurrentAttachmentPointOfFirstMonomer = + this.polymerBond.firstMonomer.getAttachmentPointByBond( + this.polymerBond, + ) === 'R1' || + this.polymerBond.firstMonomer.getPotentialAttachmentPointByBond( + this.polymerBond, + ) === 'R1'; + + // check if there is nucleotide in current row + const isBondConnectedWithNucleotide = + this.polymerBond.firstMonomer.isMonomerInRnaChainRow; + + const verticalLineLength = isBondConnectedWithNucleotide + ? RNA_CHAIN_VERTICAL_LINE_LENGTH + : VERTICAL_LINE_LENGTH + 5; + + if (this.isSecondMonomerBottomRight(startPosition, endPosition)) { + if ( + isR1TheCurrentAttachmentPointOfFirstMonomer && + reCheckAttachmentPoint + ) { + this.updateSnakeBondPath(endPosition, startPosition, false); + return; + } + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + startPosition, + ); + this.addLineFromLeftToBottom(); + this.addLine( + LineDirection.Vertical, + endPosition.y - startPosition.y - CORNER_LENGTH * 2, + ); + this.addLineFromTopToRight(); + this.addLine( + LineDirection.Horizontal, + endPosition.x - + startPosition.x - + CORNER_LENGTH * 2 - + LINE_FROM_MONOMER_LENGTH - + this.getMonomerWidth() / 2, + ); + } else if (this.isSecondMonomerTopRight(startPosition, endPosition)) { + if ( + isR1TheCurrentAttachmentPointOfFirstMonomer && + reCheckAttachmentPoint + ) { + this.updateSnakeBondPath(endPosition, startPosition, false); + return; + } + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + startPosition, + ); + this.addLineFromLeftToTop(); + this.addLine( + LineDirection.Vertical, + endPosition.y - + startPosition.y - + CORNER_LENGTH * 2 + + this.getMonomerHeight() / 2, + ); + this.addLineFromBottomToRight(); + this.addLine( + LineDirection.Horizontal, + endPosition.x - + startPosition.x - + CORNER_LENGTH * 2 - + LINE_FROM_MONOMER_LENGTH - + this.getMonomerWidth() / 2, + ); + } else if (this.isSecondMonomerBottomLeft(startPosition, endPosition)) { + if ( + isR1TheCurrentAttachmentPointOfFirstMonomer && + reCheckAttachmentPoint + ) { + this.updateSnakeBondPath(endPosition, startPosition, false); + return; + } + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + startPosition, + ); + this.addLineFromLeftToBottom(); + this.addLine(LineDirection.Vertical, verticalLineLength); + this.addLineFromTopToLeft(); + this.addLine( + LineDirection.Horizontal, + -( + startPosition.x - + endPosition.x + + LINE_FROM_MONOMER_LENGTH * 2 + + this.getMonomerWidth() + ), + ); + this.addLineFromRightToBottom(); + this.addLine( + LineDirection.Vertical, + endPosition.y - + startPosition.y - + CORNER_LENGTH * 4 - + verticalLineLength, + ); + this.addLineFromTopToRight(); + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + ); + } else if (this.isSecondMonomerTopLeft(startPosition, endPosition)) { + if ( + isR1TheCurrentAttachmentPointOfFirstMonomer && + reCheckAttachmentPoint + ) { + this.updateSnakeBondPath(endPosition, startPosition, false); + return; + } + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + startPosition, + ); + this.addLineFromLeftToBottom(); + this.addLine(LineDirection.Vertical, this.getMonomerHeight()); + this.addLineFromTopToLeft(); + this.addLine( + LineDirection.Horizontal, + -( + startPosition.x - + endPosition.x + + LINE_FROM_MONOMER_LENGTH * 2 + + this.getMonomerWidth() + ), + ); + + this.addLineFromRightToTop(); + this.addLine( + LineDirection.Vertical, + endPosition.y - startPosition.y - this.getMonomerHeight(), + ); + this.addLineFromBottomToRight(); + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + ); + } else if (this.isSecondMonomerLeft(startPosition, endPosition)) { + if ( + isR1TheCurrentAttachmentPointOfFirstMonomer && + reCheckAttachmentPoint + ) { + this.updateSnakeBondPath(endPosition, startPosition, false); + return; + } + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + startPosition, + ); + this.addLineFromLeftToBottom(); + this.addLine( + LineDirection.Vertical, + endPosition.y - startPosition.y + this.getMonomerHeight(), + ); + this.addLineFromTopToLeft(); + this.addLine( + LineDirection.Horizontal, + -( + startPosition.x - + endPosition.x + + LINE_FROM_MONOMER_LENGTH * 2 + + this.getMonomerWidth() + ), + ); + + this.addLineFromRightToTop(); + this.addLine(LineDirection.Vertical, -this.getMonomerHeight()); + this.addLineFromBottomToRight(); + this.addLine( + LineDirection.Horizontal, + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() / 2, + ); + } else { + this.addRandomLine(startPosition, endPosition); + } + } + + private isSecondMonomerTopRight( + startPosition: Vec2, + endPosition: Vec2, + ): boolean { + return ( + startPosition.y - endPosition.y > DOUBLE_CORNER_LENGTH && + endPosition.x - startPosition.x > + DOUBLE_CORNER_LENGTH + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() + ); + } + + private isSecondMonomerBottomRight( + startPosition: Vec2, + endPosition: Vec2, + ): boolean { + return ( + endPosition.y - startPosition.y > DOUBLE_CORNER_LENGTH && + endPosition.x - startPosition.x > + DOUBLE_CORNER_LENGTH + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() + ); + } + + private isSecondMonomerBottomLeft( + startPosition: Vec2, + endPosition: Vec2, + ): boolean { + return ( + endPosition.y - startPosition.y >= + 2 * (VERTICAL_LINE_LENGTH + DOUBLE_CORNER_LENGTH) && + endPosition.x - startPosition.x <= + DOUBLE_CORNER_LENGTH + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() + ); + } + + private isSecondMonomerTopLeft( + startPosition: Vec2, + endPosition: Vec2, + ): boolean { + return ( + startPosition.y - endPosition.y > 0 && + endPosition.x - startPosition.x <= + DOUBLE_CORNER_LENGTH + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() + ); + } + + private isSecondMonomerLeft(startPosition: Vec2, endPosition: Vec2): boolean { + return ( + startPosition.y - endPosition.y < 0 && + startPosition.y - endPosition.y > + -2 * (VERTICAL_LINE_LENGTH + DOUBLE_CORNER_LENGTH) && + endPosition.x - startPosition.x <= + DOUBLE_CORNER_LENGTH + LINE_FROM_MONOMER_LENGTH + this.getMonomerWidth() + ); + } + + private addLineFromTopToRight(): void { + this.path = `${this.path} c 0,4.418 3.582,${CORNER_LENGTH} ${CORNER_LENGTH},${CORNER_LENGTH}`; + } + + private addLineFromLeftToTop(): void { + this.path = `${this.path} c 4.418,0 ${CORNER_LENGTH},-3.582 ${CORNER_LENGTH},-${CORNER_LENGTH}`; + } + + private addLineFromBottomToRight(): void { + this.path = `${this.path} c 0,-4.418 3.582,-${CORNER_LENGTH} ${CORNER_LENGTH},-${CORNER_LENGTH}`; + } + + private addLineFromLeftToBottom(): void { + this.path = `${this.path} c 4.418,0 ${CORNER_LENGTH},3.582 ${CORNER_LENGTH},${CORNER_LENGTH}`; + } + + private addLineFromTopToLeft(): void { + this.path = `${this.path} c 0,4.418 -3.582,${CORNER_LENGTH} -${CORNER_LENGTH},${CORNER_LENGTH}`; + } + + private addLineFromRightToTop(): void { + this.path = `${this.path} c -4.418,0 -${CORNER_LENGTH},-3.582 -${CORNER_LENGTH},-${CORNER_LENGTH}`; + } + + private addLineFromRightToBottom(): void { + this.path = `${this.path} c -4.418,0 -${CORNER_LENGTH},3.582 -${CORNER_LENGTH},${CORNER_LENGTH}`; + } + + private addLine( + lineDirection: LineDirection, + length: number, + startPosition?: Vec2, + ): void { + const start = startPosition + ? `M ${Math.round(startPosition.x)},${Math.round(startPosition.y)}` + : this.path; + const line = + lineDirection === LineDirection.Horizontal + ? `l${length}, 0` + : `l 0, ${length}`; + this.path = `${start} ${line}`; + } + + private addRandomLine(startPosition: Vec2, endPosition: Vec2): void { + const start = `M ${Math.round(startPosition.x)},${Math.round( + startPosition.y, + )}`; + const line = `L ${Math.round(endPosition.x)},${Math.round(endPosition.y)}`; + this.path = `${start} ${line}`; + } + + // TODO: Specify the types. + public appendBondGraph(rootElement) { + this.bodyElement = rootElement + .append('line') + .attr('stroke', this.polymerBond.finished ? '#333333' : '#0097A8') + .attr('stroke-width', 1) + .attr('stroke-dasharray', '2') + .attr('class', 'selection-area') + .attr('x1', this.scaledPosition.startPosition.x) + .attr('y1', this.scaledPosition.startPosition.y) + .attr('x2', this.scaledPosition.endPosition.x) + .attr('y2', this.scaledPosition.endPosition.y) + .attr('pointer-events', 'stroke'); + + return this.bodyElement; + } + + // TODO: Specify the types. + private appendRootElement() { + return this.canvas + .insert('g', `.monomer`) + .data([this]) + .on('mouseover', (event) => { + this.editorEvents.mouseOverPolymerBond.dispatch(event); + this.editorEvents.mouseOverDrawingEntity.dispatch(event); + }) + .on('mousemove', (event) => { + this.editorEvents.mouseOnMovePolymerBond.dispatch(event); + }) + .on('mouseout', (event) => { + this.editorEvents.mouseLeavePolymerBond.dispatch(event); + this.editorEvents.mouseLeaveDrawingEntity.dispatch(event); + }) + .attr('pointer-events', 'stroke') as never as D3SvgElementSelection< + SVGGElement, + void + >; + } + + public show(_theme?: unknown, force = false): void { + if (force) { + this.sideConnectionBondTurnPoint = undefined; + } + this.rootElement = this.rootElement || this.appendRootElement(); + this.appendBond(this.rootElement); + this.appendHoverAreaElement(); + this.drawSelection(); + } + + private get isSideConnectionBondDrawn() { + return this.polymerBond.isSideChainConnection && this.path; + } + + public drawSelection(): void { + if (this.polymerBond.selected) { + this.selectionElement?.remove(); + if ( + (this.isSnakeBond && !this.isMonomersOnSameHorizontalLine()) || + this.isSideConnectionBondDrawn + ) { + this.selectionElement = this.rootElement + ?.insert('path', ':first-child') + .attr('stroke', '#57FF8F') + .attr('stroke-width', 2) + .attr('fill-opacity', 0) + .attr('d', this.path) + .attr('class', 'dynamic-element'); + } else { + this.selectionElement = this.rootElement + ?.insert('line', ':first-child') + .attr('stroke', '#57FF8F') + .attr('x1', this.scaledPosition.startPosition.x) + .attr('y1', this.scaledPosition.startPosition.y) + .attr('x2', this.scaledPosition.endPosition.x) + .attr('y2', this.scaledPosition.endPosition.y) + .attr('stroke-width', '5') + .attr('class', 'dynamic-element'); + } + } else { + this.selectionElement?.remove(); + } + } + + public moveEnd(): void { + if ( + this.isSnakeBond && + !this.isMonomersOnSameHorizontalLine() && + this.polymerBond.finished + ) { + this.moveSnakeBondEnd(); + } else { + this.moveGraphBondEnd(); + } + } + + private moveSnakeBondEnd(): void { + const startPosition = this.scaledPosition.startPosition; + const endPosition = this.scaledPosition.endPosition; + this.updateSnakeBondPath(startPosition, endPosition); + + assert(this.bodyElement); + assert(this.hoverAreaElement); + this.bodyElement.attr('d', this.path); + + this.hoverAreaElement.attr('d', this.path); + this.selectionElement?.attr('d', this.path); + } + + private moveGraphBondEnd(): void { + assert(this.bodyElement); + assert(this.hoverAreaElement); + this.bodyElement + .attr('x2', this.scaledPosition.endPosition.x) + .attr('y2', this.scaledPosition.endPosition.y); + + this.hoverAreaElement + .attr('x2', this.scaledPosition.endPosition.x) + .attr('y2', this.scaledPosition.endPosition.y); + + this.hoverCircleAreaElement + ?.attr('cx', this.scaledPosition.endPosition.x) + .attr('cy', this.scaledPosition.endPosition.y); + + this.selectionElement + ?.attr('x2', this.scaledPosition.endPosition.x) + ?.attr('y2', this.scaledPosition.endPosition.y); + } + + public moveStart(): void { + if (this.isSnakeBond && !this.isMonomersOnSameHorizontalLine()) { + this.moveSnakeBondStart(); + } else { + this.moveGraphBondStart(); + } + } + + private moveSnakeBondStart(): void { + const startPosition = this.scaledPosition.startPosition; + const endPosition = this.scaledPosition.endPosition; + this.updateSnakeBondPath(startPosition, endPosition); + + assert(this.bodyElement); + assert(this.hoverAreaElement); + this.bodyElement.attr('d', this.path); + + this.hoverAreaElement.attr('d', this.path); + this.selectionElement?.attr('d', this.path); + } + + private moveGraphBondStart(): void { + assert(this.bodyElement); + assert(this.hoverAreaElement); + this.bodyElement + .attr('x1', this.scaledPosition.startPosition.x) + .attr('y1', this.scaledPosition.startPosition.y); + + this.hoverAreaElement + .attr('x1', this.scaledPosition.startPosition.x) + .attr('y1', this.scaledPosition.startPosition.y); + + this.selectionElement + ?.attr('x1', this.scaledPosition.startPosition.x) + ?.attr('y1', this.scaledPosition.startPosition.y); + } + + protected appendHoverAreaElement(): void { + if ( + (this.isSnakeBond && !this.isMonomersOnSameHorizontalLine()) || + this.isSideConnectionBondDrawn + ) { + ( | undefined>( + this.hoverAreaElement + )) = this.rootElement + ?.append('path') + .attr('stroke', 'transparent') + .attr('d', this.path) + .attr('fill-opacity', 0) + .attr('stroke-width', '5'); + } else { + ( | undefined>( + this.hoverAreaElement + )) = this.rootElement + ?.append('line') + .attr('stroke', 'transparent') + .attr('x1', this.scaledPosition.startPosition.x) + .attr('y1', this.scaledPosition.startPosition.y) + .attr('x2', this.scaledPosition.endPosition.x) + .attr('y2', this.scaledPosition.endPosition.y) + .attr('stroke-width', '10'); + + ( | undefined>( + this.hoverCircleAreaElement + )) = this.rootElement + ?.append('circle') + .attr('cursor', 'pointer') + .attr('r', '1') + .attr('fill', 'transparent') + .attr('pointer-events', 'none') + .attr('stroke-width', '10') + .attr('cx', this.scaledPosition.endPosition.x) + .attr('cy', this.scaledPosition.endPosition.y); + } + } + + public appendHover(): void { + assert(this.bodyElement); + + const editor = CoreEditor.provideEditorInstance(); + + if (this.polymerBond.isSideChainConnection) { + const allSideConnectionBondsBodyElements = editor.canvas.querySelectorAll( + `.${SIDE_CONNECTION_BODY_ELEMENT_CLASS}`, + ); + + Array.from(allSideConnectionBondsBodyElements).forEach( + (bondBodyElement) => { + bondBodyElement.setAttribute('stroke', 'lightgrey'); + }, + ); + } + + this.bodyElement.attr('stroke', '#0097A8').attr('pointer-events', 'none'); + + if (this.polymerBond.selected && this.selectionElement) { + this.selectionElement.attr('stroke', '#CCFFDD'); + } + } + + // TODO: Specify the types. + public removeHover() { + assert(this.bodyElement); + assert(this.hoverAreaElement); + + const editor = CoreEditor.provideEditorInstance(); + + if (this.polymerBond.isSideChainConnection) { + const allSideConnectionBondsBodyElements = editor.canvas.querySelectorAll( + `.${SIDE_CONNECTION_BODY_ELEMENT_CLASS}`, + ); + + Array.from(allSideConnectionBondsBodyElements).forEach( + (bondBodyElement) => { + bondBodyElement.setAttribute('stroke', '#333333'); + }, + ); + } + + this.bodyElement.attr('stroke', '#333333').attr('pointer-events', 'stroke'); + + if (this.polymerBond.selected && this.selectionElement) { + this.selectionElement.attr('stroke', '#57FF8F'); + } + + return this.hoverAreaElement.attr('stroke', 'transparent'); + } + + private calculateIsSnakeBond(): void { + if (this.polymerBond.isSideChainConnection) { + this.isSnakeBond = false; + return; + } + + if ( + getSugarFromRnaBase(this.polymerBond.firstMonomer) || + getSugarFromRnaBase(this.polymerBond.secondMonomer) + ) { + this.isSnakeBond = false; + return; + } + + this.isSnakeBond = true; + } + + public remove(): void { + super.remove(); + if (this.polymerBond.hovered) { + this.editorEvents.mouseLeaveMonomer.dispatch(); + } + } +} + +function generateBend( + dx1: number, + dy1: number, + dx: number, + dy: number, +): string { + return `q ${SMOOTH_CORNER_SIZE * dx1},${SMOOTH_CORNER_SIZE * dy1} ${ + SMOOTH_CORNER_SIZE * dx + },${SMOOTH_CORNER_SIZE * dy} `; +} From c351e77d5ba2743847441b6130617cbaf21dfc70 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM <167520099+DmitriiP-EPAM@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:20:27 +0300 Subject: [PATCH 02/24] Update PolymerBondRendererFactory.ts --- .../PolymerBondRendererFactory.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/PolymerBondRendererFactory.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/PolymerBondRendererFactory.ts index 69e4ee7eca..bcb4a01c0a 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/PolymerBondRendererFactory.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/PolymerBondRendererFactory.ts @@ -2,7 +2,6 @@ import { CoreEditor, SnakeMode } from 'application/editor'; import { FlexModePolymerBondRenderer } from 'application/render/renderers/PolymerBondRenderer/FlexModePolymerBondRenderer'; import { SnakeModePolymerBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer'; import { PolymerBond } from 'domain/entities/PolymerBond'; -import { HydrogenBond } from 'domain/entities/HydrogenBond'; export enum LayoutMode { Flex = 'Flex', @@ -25,15 +24,13 @@ const polymerBondRendererMap = new Map< export class PolymerBondRendererFactory { public static createInstance( - polymerBond: PolymerBond | HydrogenBond, + polymerBond: PolymerBond, ): PolymerBondRendererClass { const mode = checkIfIsSnakeMode() ? LayoutMode.Snake : LayoutMode.Flex; - return polymerBond instanceof HydrogenBond - ? new SnakeModePolymerBondRenderer(polymerBond) - : (PolymerBondRendererFactory.createInstanceByMode( - mode, - polymerBond, - ) as PolymerBondRendererClass); + return PolymerBondRendererFactory.createInstanceByMode( + mode, + polymerBond, + ) as PolymerBondRendererClass; } public static createInstanceByMode( From 7cbcda76b3a46aca540d8a76b76360c44814e54f Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM <167520099+DmitriiP-EPAM@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:21:10 +0300 Subject: [PATCH 03/24] Update SnakeModePolymerBondRenderer.ts --- .../SnakeModePolymerBondRenderer.ts | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 2391b88b97..14ed49e681 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -19,8 +19,6 @@ import { DrawingEntity } from 'domain/entities/DrawingEntity'; import { PolymerBond } from 'domain/entities/PolymerBond'; import { getSugarFromRnaBase } from 'domain/helpers/monomers'; import { BaseRenderer } from '../BaseRenderer'; -import { HydrogenBond } from 'domain/entities/HydrogenBond'; -import { SnakeMode } from 'application/editor'; enum LineDirection { Horizontal = 'Horizontal', @@ -39,9 +37,9 @@ const SMOOTH_CORNER_SIZE = 5; const SIDE_CONNECTION_BODY_ELEMENT_CLASS = 'polymer-bond-body'; // TODO: Need to split the class by three: -// - SnakeModeBackboneBondRenderer (black “snake” line) -// - SnakeModeSideChainBondRenderer (blue “snake” line) -// - SnakeModeRNABaseAndSugarBondRenderer (black straight line) +// - `SnakeModeBackboneBondRenderer` (black “snake” line) +// - `SnakeModeSideChainBondRenderer` (blue “snake” line) +// - `SnakeModeRNABaseAndSugarBondRenderer` (black straight line) export class SnakeModePolymerBondRenderer extends BaseRenderer { private editorEvents: typeof editorEvents; private isSnakeBond = false; // `SnakeModeBackboneBondRenderer` or `SnakeModeRNABaseAndSugarBondRenderer`. @@ -64,10 +62,6 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { return true; } - private get isHydrogenBond() { - return this.polymerBond instanceof HydrogenBond; - } - public get rootBBox(): DOMRect | undefined { const rootNode = this.rootElement?.node(); if (!rootNode) return; @@ -125,11 +119,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { const matrix = editor.drawingEntitiesManager.canvasMatrix; const cells = matrix?.polymerBondToCells.get(this.polymerBond); - if ( - this.polymerBond.isSideChainConnection && - (!this.isHydrogenBond || editor.mode instanceof SnakeMode) && - cells - ) { + if (this.polymerBond.isSideChainConnection && cells) { this.appendSideConnectionBond(rootElement, cells); } else if ( this.isSnakeBond && @@ -375,11 +365,11 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.bodyElement = rootElement .append('path') .attr('class', `${SIDE_CONNECTION_BODY_ELEMENT_CLASS}`) - .attr('stroke', this.isHydrogenBond ? '#333333' : '#43B5C0') + .attr('stroke', '#43B5C0') .attr('stroke-width', 1) .attr('d', dAttributeForPath) .attr('fill', 'none') - .attr('stroke-dasharray', this.isHydrogenBond ? '2' : '0') + .attr('stroke-dasharray', '0') .attr('pointer-events', 'all'); this.path = dAttributeForPath; @@ -709,7 +699,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { .append('line') .attr('stroke', this.polymerBond.finished ? '#333333' : '#0097A8') .attr('stroke-width', 1) - .attr('stroke-dasharray', this.isHydrogenBond ? '2' : '0') + .attr('stroke-dasharray', '0') .attr('class', 'selection-area') .attr('x1', this.scaledPosition.startPosition.x) .attr('y1', this.scaledPosition.startPosition.y) @@ -919,10 +909,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { Array.from(allSideConnectionBondsBodyElements).forEach( (bondBodyElement) => { - bondBodyElement.setAttribute( - 'stroke', - this.isHydrogenBond ? 'lightgrey' : '#C0E2E6', - ); + bondBodyElement.setAttribute('stroke', '#C0E2E6'); }, ); } @@ -953,9 +940,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { bondBodyElement.setAttribute( 'stroke', - renderer.polymerBond.isSideChainConnection && !this.isHydrogenBond - ? '#43B5C0' - : '#333333', + renderer.polymerBond.isSideChainConnection ? '#43B5C0' : '#333333', ); }, ); @@ -964,9 +949,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.bodyElement .attr( 'stroke', - this.polymerBond.isSideChainConnection && !this.isHydrogenBond - ? '#43B5C0' - : '#333333', + this.polymerBond.isSideChainConnection && true ? '#43B5C0' : '#333333', ) .attr('pointer-events', 'stroke'); From 4a75608884f7b250db31d20a239abe8afb0d4bea Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM <167520099+DmitriiP-EPAM@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:21:34 +0300 Subject: [PATCH 04/24] Update RenderersManager.ts --- .../render/renderers/RenderersManager.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/RenderersManager.ts b/packages/ketcher-core/src/application/render/renderers/RenderersManager.ts index 518a4ee9df..9eb6b30d40 100644 --- a/packages/ketcher-core/src/application/render/renderers/RenderersManager.ts +++ b/packages/ketcher-core/src/application/render/renderers/RenderersManager.ts @@ -10,9 +10,16 @@ import { notifyRenderComplete } from 'application/render/internal'; import { BaseMonomerRenderer } from 'application/render/renderers/BaseMonomerRenderer'; import { FlexModePolymerBondRenderer } from 'application/render/renderers/PolymerBondRenderer/FlexModePolymerBondRenderer'; import { PolymerBondRendererFactory } from 'application/render/renderers/PolymerBondRenderer/PolymerBondRendererFactory'; +import { SnakeModeHydrogenBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer'; import { SnakeModePolymerBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer'; import assert from 'assert'; -import { Peptide, Phosphate, Sugar, UnsplitNucleotide } from 'domain/entities'; +import { + HydrogenBond, + Peptide, + Phosphate, + Sugar, + UnsplitNucleotide, +} from 'domain/entities'; import { BaseMonomer } from 'domain/entities/BaseMonomer'; import { Command } from 'domain/entities/Command'; import { DrawingEntity } from 'domain/entities/DrawingEntity'; @@ -48,7 +55,7 @@ export class RenderersManager { public polymerBonds = new Map< number, - FlexModeOrSnakeModePolymerBondRenderer + FlexModeOrSnakeModePolymerBondRenderer | SnakeModeHydrogenBondRenderer >(); public atoms = new Map(); @@ -136,7 +143,9 @@ export class RenderersManager { public addPolymerBond(polymerBond: PolymerBond): void { const polymerBondRenderer = - PolymerBondRendererFactory.createInstance(polymerBond); + polymerBond instanceof HydrogenBond + ? new SnakeModeHydrogenBondRenderer(polymerBond) + : PolymerBondRendererFactory.createInstance(polymerBond); this.polymerBonds.set(polymerBond.id, polymerBondRenderer); polymerBondRenderer.show(); polymerBondRenderer.polymerBond.firstMonomer.renderer?.redrawAttachmentPoints(); @@ -392,7 +401,9 @@ export class RenderersManager { assert(polymerBond.secondMonomer); const polymerBondRenderer = - PolymerBondRendererFactory.createInstance(polymerBond); + polymerBond instanceof HydrogenBond + ? new SnakeModeHydrogenBondRenderer(polymerBond) + : PolymerBondRendererFactory.createInstance(polymerBond); this.polymerBonds.set(polymerBond.id, polymerBondRenderer); this.markForReEnumeration(); this.markForRecalculateBegin(); From 7c04b5e2bc6936f81359e9a36f613d5ffdbdd966 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM <167520099+DmitriiP-EPAM@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:22:11 +0300 Subject: [PATCH 05/24] Update PolymerBond.ts --- packages/ketcher-core/src/domain/entities/PolymerBond.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/ketcher-core/src/domain/entities/PolymerBond.ts b/packages/ketcher-core/src/domain/entities/PolymerBond.ts index 15438c2f7a..9b45c47233 100644 --- a/packages/ketcher-core/src/domain/entities/PolymerBond.ts +++ b/packages/ketcher-core/src/domain/entities/PolymerBond.ts @@ -1,5 +1,6 @@ import { BaseRenderer } from 'application/render/renderers/BaseRenderer'; import { FlexModePolymerBondRenderer } from 'application/render/renderers/PolymerBondRenderer/FlexModePolymerBondRenderer'; +import { SnakeModeHydrogenBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer'; import { SnakeModePolymerBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer'; import { BackBoneBondSequenceRenderer } from 'application/render/renderers/sequence/BackBoneBondSequenceRenderer'; import { PolymerBondSequenceRenderer } from 'application/render/renderers/sequence/PolymerBondSequenceRenderer'; @@ -20,7 +21,9 @@ export type FlexOrSequenceOrSnakeModePolymerBondRenderer = export class PolymerBond extends BaseBond { public secondMonomer?: BaseMonomer; - public renderer?: FlexOrSequenceOrSnakeModePolymerBondRenderer = undefined; + public renderer?: + | FlexOrSequenceOrSnakeModePolymerBondRenderer + | SnakeModeHydrogenBondRenderer = undefined; constructor(public firstMonomer: BaseMonomer, secondMonomer?: BaseMonomer) { super(); @@ -37,7 +40,9 @@ export class PolymerBond extends BaseBond { } public setRenderer( - renderer: FlexOrSequenceOrSnakeModePolymerBondRenderer, + renderer: + | FlexOrSequenceOrSnakeModePolymerBondRenderer + | SnakeModeHydrogenBondRenderer, ): void { super.setBaseRenderer(renderer as BaseRenderer); this.renderer = renderer; From 94e5c71ccb50c54d44ee6fe1cfd5a892d3a8b1d0 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM <167520099+DmitriiP-EPAM@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:05:52 +0300 Subject: [PATCH 06/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?PolymerBondRenderer):=20Refactor=20`generateBend(4)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PolymerBondRenderer/SnakeModePolymerBondRenderer.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 14ed49e681..01eccea003 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -991,7 +991,9 @@ function generateBend( dx: number, dy: number, ): string { - return `q ${SMOOTH_CORNER_SIZE * dx1},${SMOOTH_CORNER_SIZE * dy1} ${ - SMOOTH_CORNER_SIZE * dx - },${SMOOTH_CORNER_SIZE * dy} `; + const controlPoint = `${SMOOTH_CORNER_SIZE * dx1},${ + SMOOTH_CORNER_SIZE * dy1 + }`; + const endPoint = `${SMOOTH_CORNER_SIZE * dx},${SMOOTH_CORNER_SIZE * dy}`; + return `q ${controlPoint} ${endPoint} `; } From d6e6dd7bd939103ffe3c90871c23db20a2af4788 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM <167520099+DmitriiP-EPAM@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:07:10 +0300 Subject: [PATCH 07/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?HydrogenBondRenderer):=20Refactor=20`generateBend(4)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts index 3ef890c57c..45d1c34733 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts @@ -982,7 +982,9 @@ function generateBend( dx: number, dy: number, ): string { - return `q ${SMOOTH_CORNER_SIZE * dx1},${SMOOTH_CORNER_SIZE * dy1} ${ - SMOOTH_CORNER_SIZE * dx - },${SMOOTH_CORNER_SIZE * dy} `; + const controlPoint = `${SMOOTH_CORNER_SIZE * dx1},${ + SMOOTH_CORNER_SIZE * dy1 + }`; + const endPoint = `${SMOOTH_CORNER_SIZE * dx},${SMOOTH_CORNER_SIZE * dy}`; + return `q ${controlPoint} ${endPoint} `; } From 1dcc4b14c54fa146d16e789fa4aa2e77bf351373 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Tue, 3 Dec 2024 18:14:23 +0300 Subject: [PATCH 08/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(BaseRende?= =?UTF-8?q?rer.ts):=20Create=20`selectionElement`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/application/render/renderers/BaseRenderer.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts b/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts index 218afd084a..d7c2ace76c 100644 --- a/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts @@ -29,6 +29,7 @@ export abstract class BaseRenderer implements IBaseRenderer { | D3SvgElementSelection | D3SvgElementSelection; + // An extra invisible area around `bodyElement` to make it easier for a user to hover over it. protected hoverAreaElement?: D3SvgElementSelection< SVGGElement | SVGLineElement, void @@ -39,6 +40,11 @@ export abstract class BaseRenderer implements IBaseRenderer { void >; + protected selectionElement?: + | D3SvgElementSelection + | D3SvgElementSelection + | D3SvgElementSelection; + protected canvasWrapper: D3SvgElementSelection; protected canvas: D3SvgElementSelection; From 2cda4d4f647fa47f2b038ec109e3837500a8fe0d Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Tue, 3 Dec 2024 23:51:31 +0300 Subject: [PATCH 09/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(BaseRende?= =?UTF-8?q?rer.ts):=20Cancel=20the=20creation=20of=20`selectionElement`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/application/render/renderers/BaseRenderer.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts b/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts index d7c2ace76c..ede7d5f549 100644 --- a/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/BaseRenderer.ts @@ -40,11 +40,6 @@ export abstract class BaseRenderer implements IBaseRenderer { void >; - protected selectionElement?: - | D3SvgElementSelection - | D3SvgElementSelection - | D3SvgElementSelection; - protected canvasWrapper: D3SvgElementSelection; protected canvas: D3SvgElementSelection; From 6f64110c74d1480120c9add3ac1f2404ed29a2f4 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Tue, 3 Dec 2024 23:56:08 +0300 Subject: [PATCH 10/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(types.ts)?= =?UTF-8?q?:=20Rename=20`Datum`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ketcher-core/src/application/render/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ketcher-core/src/application/render/types.ts b/packages/ketcher-core/src/application/render/types.ts index d5e90a47cc..1c70a43ef0 100644 --- a/packages/ketcher-core/src/application/render/types.ts +++ b/packages/ketcher-core/src/application/render/types.ts @@ -2,5 +2,5 @@ import { Selection } from 'd3'; export type D3SvgElementSelection< ElementType extends SVGElement, - T, -> = Selection; + Datum, +> = Selection; From db02fead130d7dd9f5fc8e7b090f7d77e6d16b24 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Wed, 4 Dec 2024 00:04:29 +0300 Subject: [PATCH 11/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?PolymerBondRenderer):=20Create=20and=20use=20`SideChainConnecti?= =?UTF-8?q?onBondRenderer`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SideChainConnectionBondRenderer.ts | 314 ++++++++++++++++++ .../SnakeModePolymerBondRenderer.ts | 281 ++-------------- 2 files changed, 345 insertions(+), 250 deletions(-) create mode 100644 packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts new file mode 100644 index 0000000000..48ca42d68a --- /dev/null +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts @@ -0,0 +1,314 @@ +import { + BaseMonomerRenderer, + BaseSequenceItemRenderer, +} from 'application/render'; +import { D3SvgElementSelection } from 'application/render/types'; +import { BaseMonomer, PolymerBond, Vec2 } from 'domain/entities'; +import { Cell } from 'domain/entities/canvas-matrix/Cell'; +import { + Connection, + ConnectionDirectionInDegrees, + ConnectionDirectionOfLastCell, +} from 'domain/entities/canvas-matrix/Connection'; +import { SNAKE_LAYOUT_CELL_WIDTH } from 'domain/entities/DrawingEntitiesManager'; + +type AppendPathToElementFunction = ( + element: D3SvgElementSelection, + cssClassForPath: string, +) => D3SvgElementSelection; + +interface AppendSideConnectionBondParameter { + readonly cells: readonly Cell[]; + readonly polymerBond: PolymerBond; + readonly scaledPosition: { + readonly endPosition: Vec2; + readonly startPosition: Vec2; + }; + readonly sideConnectionBondTurnPoint?: number; +} + +interface AppendSideConnectionBondResult { + readonly appendPathToElement: AppendPathToElementFunction; + readonly pathDAttributeValue: string; +} + +const BOND_END_LENGTH = 15; +const CELL_HEIGHT = 40; +const SMOOTH_CORNER_SIZE = 5; + +// TODO: Can be: +// - a class with static methods. +// - a set of functions. +export class SideChainConnectionBondRenderer { + public appendSideConnectionBond({ + cells, + polymerBond, + scaledPosition, + sideConnectionBondTurnPoint, + }: AppendSideConnectionBondParameter): AppendSideConnectionBondResult { + const firstCell = cells[0]; + const firstCellConnection = firstCell.connections.find((connection) => { + return connection.polymerBond === polymerBond; + }) as Connection; + const isVerticalConnection = firstCellConnection.isVertical; + const isStraightVerticalConnection = + cells.length === 2 && isVerticalConnection; + const isFirstMonomerOfBondInFirstCell = firstCell.node?.monomers.includes( + polymerBond.firstMonomer, + ); + const isTwoNeighborRowsConnection = cells.every( + (cell) => cell.y === firstCell.y || cell.y === firstCell.y + 1, + ); + const startPosition = isFirstMonomerOfBondInFirstCell + ? scaledPosition.startPosition + : scaledPosition.endPosition; + const endPosition = isFirstMonomerOfBondInFirstCell + ? scaledPosition.endPosition + : scaledPosition.startPosition; + const xDirection = + startPosition.x >= (sideConnectionBondTurnPoint || endPosition.x) + ? 180 + : 0; + let pathDAttributeValue = `M ${startPosition.x},${startPosition.y} `; + + const cos = Math.cos((xDirection * Math.PI) / 180); + + let previousConnection: Connection; + let previousCell: Cell; + + const horizontalPartIntersectionsOffset = firstCellConnection.xOffset; + + const areCellsOnSameRow = cells.every((cell) => { + return cell.y === firstCell.y; + }); + const isSecondCellEmpty = cells[1].node === null; + + if (areCellsOnSameRow) { + pathDAttributeValue += `L ${startPosition.x},${ + startPosition.y - + BOND_END_LENGTH - + horizontalPartIntersectionsOffset * 3 + } `; + pathDAttributeValue += generateBend(0, -1, cos, -1); + } else { + pathDAttributeValue += `L ${startPosition.x},${ + startPosition.y + + BOND_END_LENGTH + + horizontalPartIntersectionsOffset * 3 + } `; + if ( + !isStraightVerticalConnection && + !isSecondCellEmpty && + !isTwoNeighborRowsConnection + ) { + pathDAttributeValue += generateBend(0, 1, cos, 1); + } + } + + if (isVerticalConnection && !isStraightVerticalConnection) { + const direction = + sideConnectionBondTurnPoint && + startPosition.x < sideConnectionBondTurnPoint + ? 0 + : 180; + const result = this.drawPartOfSideConnection({ + cell: firstCell, + connection: firstCellConnection, + direction, + horizontal: true, + sideConnectionBondTurnPoint: sideConnectionBondTurnPoint ?? 0, + }); + pathDAttributeValue += result.pathPart; + sideConnectionBondTurnPoint = result.sideConnectionBondTurnPoint; + } + + let maxHorizontalOffset = 0; + + cells.forEach((cell: Cell, cellIndex: number): void => { + const cellConnection = cell.connections.find( + (connection: Connection): boolean => { + return connection.polymerBond === polymerBond; + }, + ) as Connection; + const isLastCell = cellIndex === cells.length - 1; + const _xDirection = sideConnectionBondTurnPoint + ? endPosition.x < sideConnectionBondTurnPoint + ? 180 + : 0 + : xDirection; + const maxXOffset = cell.connections.reduce( + (max: number, connection: Connection): number => { + return connection.isVertical || max > connection.xOffset + ? max + : connection.xOffset; + }, + 0, + ); + + maxHorizontalOffset = + maxHorizontalOffset > maxXOffset ? maxHorizontalOffset : maxXOffset; + + if (isLastCell) { + if (isStraightVerticalConnection) { + return; + } + + const directionObject = + cellConnection.direction as ConnectionDirectionOfLastCell; + const yDirection = isVerticalConnection ? 90 : directionObject.y; + const sin = Math.sin((yDirection * Math.PI) / 180); + const cos = Math.cos((_xDirection * Math.PI) / 180); + + if (!areCellsOnSameRow) { + pathDAttributeValue += `V ${ + endPosition.y - + CELL_HEIGHT / 2 - + SMOOTH_CORNER_SIZE - + sin * (cellConnection.yOffset || 0) * 3 - + (isTwoNeighborRowsConnection + ? maxHorizontalOffset - cellConnection.xOffset + : cellConnection.xOffset) * + 3 + } `; + pathDAttributeValue += generateBend(0, sin, cos, 1); + } + pathDAttributeValue += `H ${endPosition.x - SMOOTH_CORNER_SIZE * cos} `; + pathDAttributeValue += generateBend(cos, 0, cos, 1); + return; + } + + // Empty cells. + if (cell.node === null) { + return; + } + + // Other cells. + if ( + previousConnection && + previousConnection.direction !== cellConnection.direction + ) { + // TODO?: Check. I am not sure about `as DirectionInDegrees`. + const horizontal = new Set([0, 180]).has( + previousConnection.direction as ConnectionDirectionInDegrees, + ); + const direction = horizontal + ? xDirection + : // TODO?: Check. I am not sure about `as DirectionInDegrees`. + (previousConnection.direction as ConnectionDirectionInDegrees); + const result = this.drawPartOfSideConnection({ + cell: previousCell, + connection: previousConnection, + direction, + horizontal, + sideConnectionBondTurnPoint: sideConnectionBondTurnPoint ?? 0, + }); + pathDAttributeValue += result.pathPart; + sideConnectionBondTurnPoint = result.sideConnectionBondTurnPoint; + } + previousCell = cell; + previousConnection = cellConnection; + }); + + pathDAttributeValue += `L ${endPosition.x},${endPosition.y} `; + + const appendPathToElement: AppendPathToElementFunction = ( + element: D3SvgElementSelection, + cssClassForPath: string, + ): D3SvgElementSelection => { + const pathElement: D3SvgElementSelection = + element.append('path'); + return pathElement + .attr('class', cssClassForPath) + .attr('d', pathDAttributeValue) + .attr('pointer-events', 'stroke') + .attr('stroke', '#43b5c0') + .attr('stroke-dasharray', '0') + .attr('stroke-width', 1) + .attr('fill', 'none'); + }; + + return { + appendPathToElement, + pathDAttributeValue, + }; + } + + // TODO: Specify the types. + private drawPartOfSideConnection({ + cell, + connection, + direction, + horizontal, + sideConnectionBondTurnPoint, + }: { + readonly cell: Cell; + readonly connection: Connection; + readonly direction: ConnectionDirectionInDegrees; + readonly horizontal: boolean; + readonly sideConnectionBondTurnPoint: number; + }): { + readonly pathPart: string; + readonly sideConnectionBondTurnPoint: number; + } { + const sin = Math.sin((direction * Math.PI) / 180); + const cos = Math.cos((direction * Math.PI) / 180); + const xOffset = (SNAKE_LAYOUT_CELL_WIDTH / 2) * cos; + const yOffset = (CELL_HEIGHT / 2) * sin; + const maxXOffset = cell.connections.reduce( + (max: number, connection: Connection): number => { + return max > connection.xOffset ? max : connection.xOffset; + }, + 0, + ); + const maxYOffset = cell.connections.reduce( + (max: number, connection: Connection): number => { + const connectionYOffset = connection.yOffset || 0; + return max > connectionYOffset ? max : connectionYOffset; + }, + 0, + ); + + let endOfPathPart: number; + if (horizontal && sideConnectionBondTurnPoint) { + endOfPathPart = sideConnectionBondTurnPoint; + } else { + const { monomerSize, scaledMonomerPosition } = ( + cell.monomer as BaseMonomer + ).renderer as BaseMonomerRenderer | BaseSequenceItemRenderer; + endOfPathPart = horizontal + ? scaledMonomerPosition.x + monomerSize.width / 2 + xOffset + : scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; + } + + const sideConnectionBondTurnPointInternal = endOfPathPart; + + if (horizontal) { + endOfPathPart += + -(connection.yOffset || 0) * 3 + + cos * -connection.xOffset * 3 + + cos * (maxXOffset + 1) * 3 + + (maxYOffset + 1) * 3; + } + let pathPart = horizontal ? 'H ' : 'V '; + pathPart += `${endOfPathPart - SMOOTH_CORNER_SIZE * cos} `; + pathPart += generateBend(cos, sin, cos, 1); + + return { + pathPart, + sideConnectionBondTurnPoint: sideConnectionBondTurnPointInternal, + }; + } +} + +function generateBend( + dx1: number, + dy1: number, + dx: number, + dy: number, +): string { + const controlPoint = `${SMOOTH_CORNER_SIZE * dx1},${ + SMOOTH_CORNER_SIZE * dy1 + }`; + const endPoint = `${SMOOTH_CORNER_SIZE * dx},${SMOOTH_CORNER_SIZE * dy}`; + return `q ${controlPoint} ${endPoint} `; +} diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 01eccea003..b44853acba 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -1,20 +1,11 @@ import { editorEvents } from 'application/editor/editorEvents'; import { CoreEditor } from 'application/editor/internal'; import { Coordinates } from 'application/editor/shared/coordinates'; -import { - BaseMonomerRenderer, - BaseSequenceItemRenderer, -} from 'application/render'; +import { SideChainConnectionBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer'; import { D3SvgElementSelection } from 'application/render/types'; import assert from 'assert'; -import { BaseMonomer, Vec2 } from 'domain/entities'; +import { Vec2 } from 'domain/entities'; import { Cell } from 'domain/entities/canvas-matrix/Cell'; -import { - Connection, - ConnectionDirectionInDegrees, - ConnectionDirectionOfLastCell, -} from 'domain/entities/canvas-matrix/Connection'; -import { SNAKE_LAYOUT_CELL_WIDTH } from 'domain/entities/DrawingEntitiesManager'; import { DrawingEntity } from 'domain/entities/DrawingEntity'; import { PolymerBond } from 'domain/entities/PolymerBond'; import { getSugarFromRnaBase } from 'domain/helpers/monomers'; @@ -31,9 +22,6 @@ const RNA_CHAIN_VERTICAL_LINE_LENGTH = 74; const CORNER_LENGTH = 4; const DOUBLE_CORNER_LENGTH = CORNER_LENGTH * 2; -const BOND_END_LENGTH = 15; -const CELL_HEIGHT = 40; -const SMOOTH_CORNER_SIZE = 5; const SIDE_CONNECTION_BODY_ELEMENT_CLASS = 'polymer-bond-body'; // TODO: Need to split the class by three: @@ -41,14 +29,22 @@ const SIDE_CONNECTION_BODY_ELEMENT_CLASS = 'polymer-bond-body'; // - `SnakeModeSideChainBondRenderer` (blue “snake” line) // - `SnakeModeRNABaseAndSugarBondRenderer` (black straight line) export class SnakeModePolymerBondRenderer extends BaseRenderer { + public declare bodyElement?: D3SvgElementSelection< + SVGLineElement | SVGPathElement, + this + >; + + // TODO: Specify the type of `selectionElement`. + // private selectionElement?: + // | D3SvgElementSelection + // | D3SvgElementSelection; + private selectionElement; + private editorEvents: typeof editorEvents; private isSnakeBond = false; // `SnakeModeBackboneBondRenderer` or `SnakeModeRNABaseAndSugarBondRenderer`. - // TODO: Specify the types. - private selectionElement; private path = ''; private previousStateOfIsMonomersOnSameHorizontalLine = false; private sideConnectionBondTurnPoint?: number; - public declare bodyElement?: D3SvgElementSelection; constructor(public readonly polymerBond: PolymerBond) { super(polymerBond as DrawingEntity); @@ -151,228 +147,26 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { return this.bodyElement; } - private drawPartOfSideConnection( - isHorizontal: boolean, - connection: Connection, - cell: Cell, - direction: ConnectionDirectionInDegrees, - ): string { - const sin = Math.sin((direction * Math.PI) / 180); - const cos = Math.cos((direction * Math.PI) / 180); - const xOffset = (SNAKE_LAYOUT_CELL_WIDTH / 2) * cos; - const yOffset = (CELL_HEIGHT / 2) * sin; - const maxXOffset = cell.connections.reduce( - (max: number, connection: Connection): number => { - return max > connection.xOffset ? max : connection.xOffset; - }, - 0, - ); - const maxYOffset = cell.connections.reduce( - (max: number, connection: Connection): number => { - const connectionYOffset = connection.yOffset || 0; - return max > connectionYOffset ? max : connectionYOffset; - }, - 0, - ); - - let endOfPathPart: number; - if (isHorizontal && this.sideConnectionBondTurnPoint) { - endOfPathPart = this.sideConnectionBondTurnPoint; - } else { - const { monomerSize, scaledMonomerPosition } = ( - cell.monomer as BaseMonomer - ).renderer as BaseMonomerRenderer | BaseSequenceItemRenderer; - endOfPathPart = isHorizontal - ? scaledMonomerPosition.x + monomerSize.width / 2 + xOffset - : scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; - } - - this.sideConnectionBondTurnPoint = endOfPathPart; - - if (isHorizontal) { - endOfPathPart += - -(connection.yOffset || 0) * 3 + - cos * -connection.xOffset * 3 + - cos * (maxXOffset + 1) * 3 + - (maxYOffset + 1) * 3; - } - let pathPart = isHorizontal ? 'H ' : 'V '; - pathPart += `${endOfPathPart - SMOOTH_CORNER_SIZE * cos} `; - pathPart += generateBend(cos, sin, cos, 1); - - return pathPart; - } + private appendSideConnectionBond( + rootElement: D3SvgElementSelection, + cells: Cell[], + ): D3SvgElementSelection { + const sideChainConnectionBondRenderer = + new SideChainConnectionBondRenderer(); + const { appendPathToElement, pathDAttributeValue } = + sideChainConnectionBondRenderer.appendSideConnectionBond({ + cells, + polymerBond: this.polymerBond, + scaledPosition: this.scaledPosition, + sideConnectionBondTurnPoint: this.sideConnectionBondTurnPoint, + }); - // TODO: Specify the types. - private appendSideConnectionBond(rootElement, cells: Cell[]) { - const firstCell = cells[0]; - const firstCellConnection = firstCell.connections.find( - (connection: Connection): boolean => { - return connection.polymerBond === this.polymerBond; - }, - ) as Connection; - const isVerticalConnection = firstCellConnection.isVertical; - const isStraightVerticalConnection = - cells.length === 2 && isVerticalConnection; - const isFirstMonomerOfBondInFirstCell = firstCell.node?.monomers.includes( - this.polymerBond.firstMonomer, - ); - const isTwoNeighborRowsConnection = cells.every( - (cell) => cell.y === firstCell.y || cell.y === firstCell.y + 1, - ); - const startPosition = isFirstMonomerOfBondInFirstCell - ? this.scaledPosition.startPosition - : this.scaledPosition.endPosition; - const endPosition = isFirstMonomerOfBondInFirstCell - ? this.scaledPosition.endPosition - : this.scaledPosition.startPosition; - const xDirection = - startPosition.x >= (this.sideConnectionBondTurnPoint || endPosition.x) - ? 180 - : 0; - let dAttributeForPath = `M ${startPosition.x},${startPosition.y} `; - - const cos = Math.cos((xDirection * Math.PI) / 180); - - let previousConnection: Connection; - let previousCell: Cell; - - const horizontalPartIntersectionsOffset = firstCellConnection.xOffset; - - const areCellsOnSameRow = cells.every((cell) => { - return cell.y === firstCell.y; - }); - const isSecondCellEmpty = cells[1].node === null; - - if (areCellsOnSameRow) { - dAttributeForPath += `L ${startPosition.x},${ - startPosition.y - - BOND_END_LENGTH - - horizontalPartIntersectionsOffset * 3 - } `; - dAttributeForPath += generateBend(0, -1, cos, -1); - } else { - dAttributeForPath += `L ${startPosition.x},${ - startPosition.y + - BOND_END_LENGTH + - horizontalPartIntersectionsOffset * 3 - } `; - if ( - !isStraightVerticalConnection && - !isSecondCellEmpty && - !isTwoNeighborRowsConnection - ) { - dAttributeForPath += generateBend(0, 1, cos, 1); - } - } + this.bodyElement = appendPathToElement( + rootElement, + SIDE_CONNECTION_BODY_ELEMENT_CLASS, + ).data([this]); - if (isVerticalConnection && !isStraightVerticalConnection) { - dAttributeForPath += this.drawPartOfSideConnection( - true, - firstCellConnection, - firstCell, - this.sideConnectionBondTurnPoint && - startPosition.x < this.sideConnectionBondTurnPoint - ? 0 - : 180, - ); - } - - let maxHorizontalOffset = 0; - - cells.forEach((cell: Cell, cellIndex: number): void => { - const cellConnection = cell.connections.find( - (connection: Connection): boolean => { - return connection.polymerBond === this.polymerBond; - }, - ) as Connection; - const isLastCell = cellIndex === cells.length - 1; - const _xDirection = this.sideConnectionBondTurnPoint - ? endPosition.x < this.sideConnectionBondTurnPoint - ? 180 - : 0 - : xDirection; - const maxXOffset = cell.connections.reduce( - (max: number, connection: Connection): number => { - return connection.isVertical || max > connection.xOffset - ? max - : connection.xOffset; - }, - 0, - ); - - maxHorizontalOffset = - maxHorizontalOffset > maxXOffset ? maxHorizontalOffset : maxXOffset; - - if (isLastCell) { - if (isStraightVerticalConnection) { - return; - } - - const directionObject = - cellConnection.direction as ConnectionDirectionOfLastCell; - const yDirection = isVerticalConnection ? 90 : directionObject.y; - const sin = Math.sin((yDirection * Math.PI) / 180); - const cos = Math.cos((_xDirection * Math.PI) / 180); - - if (!areCellsOnSameRow) { - dAttributeForPath += `V ${ - endPosition.y - - CELL_HEIGHT / 2 - - SMOOTH_CORNER_SIZE - - sin * (cellConnection.yOffset || 0) * 3 - - (isTwoNeighborRowsConnection - ? maxHorizontalOffset - cellConnection.xOffset - : cellConnection.xOffset) * - 3 - } `; - dAttributeForPath += generateBend(0, sin, cos, 1); - } - dAttributeForPath += `H ${endPosition.x - SMOOTH_CORNER_SIZE * cos} `; - dAttributeForPath += generateBend(cos, 0, cos, 1); - return; - } - // empty cells - if (cell.node === null) { - return; - } - - // other cells - if ( - previousConnection && - previousConnection.direction !== cellConnection.direction - ) { - const isHorizontal = - previousConnection.direction === 0 || - previousConnection.direction === 180; - - dAttributeForPath += this.drawPartOfSideConnection( - isHorizontal, - previousConnection, - previousCell, - // FIXME: Check. Is it correct to use `as ConnectionDirectionInDegrees` here? - isHorizontal - ? xDirection - : (previousConnection.direction as ConnectionDirectionInDegrees), - ); - } - previousCell = cell; - previousConnection = cellConnection; - }); - - dAttributeForPath += `L ${endPosition.x},${endPosition.y} `; - - this.bodyElement = rootElement - .append('path') - .attr('class', `${SIDE_CONNECTION_BODY_ELEMENT_CLASS}`) - .attr('stroke', '#43B5C0') - .attr('stroke-width', 1) - .attr('d', dAttributeForPath) - .attr('fill', 'none') - .attr('stroke-dasharray', '0') - .attr('pointer-events', 'all'); - - this.path = dAttributeForPath; + this.path = pathDAttributeValue; return this.bodyElement; } @@ -984,16 +778,3 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { } } } - -function generateBend( - dx1: number, - dy1: number, - dx: number, - dy: number, -): string { - const controlPoint = `${SMOOTH_CORNER_SIZE * dx1},${ - SMOOTH_CORNER_SIZE * dy1 - }`; - const endPoint = `${SMOOTH_CORNER_SIZE * dx},${SMOOTH_CORNER_SIZE * dy}`; - return `q ${controlPoint} ${endPoint} `; -} From 557aab8dbc2dcf24f8bd0d3dc64dae9249bd5d7f Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Wed, 4 Dec 2024 11:14:51 +0300 Subject: [PATCH 12/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(PolymerBo?= =?UTF-8?q?nd):=20Correct=20by=20the=20code=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ketcher-core/src/domain/entities/PolymerBond.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ketcher-core/src/domain/entities/PolymerBond.ts b/packages/ketcher-core/src/domain/entities/PolymerBond.ts index 07bb72a571..e01e40d3fa 100644 --- a/packages/ketcher-core/src/domain/entities/PolymerBond.ts +++ b/packages/ketcher-core/src/domain/entities/PolymerBond.ts @@ -24,6 +24,7 @@ export class PolymerBond extends BaseBond { public renderer?: | FlexOrSequenceOrSnakeModePolymerBondRenderer | SnakeModeHydrogenBondRenderer = undefined; + public restOfRowsWithAntisense?: number = undefined; constructor(public firstMonomer: BaseMonomer, secondMonomer?: BaseMonomer) { From 6c4481c957dfec9df527fce07d03544edb78a404 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Wed, 4 Dec 2024 11:26:16 +0300 Subject: [PATCH 13/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?HydrogenBondRenderer):=20Specify=20the=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts | 7 +++---- .../PolymerBondRenderer/SnakeModePolymerBondRenderer.ts | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts index 45d1c34733..1664bcda06 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts @@ -111,8 +111,9 @@ export class SnakeModeHydrogenBondRenderer extends BaseRenderer { this.isMonomersOnSameHorizontalLine(); } - // TODO: Specify the types. - public appendBond(rootElement) { + public appendBond( + rootElement: D3SvgElementSelection, + ): void { const editor = CoreEditor.provideEditorInstance(); const matrix = editor.drawingEntitiesManager.canvasMatrix; const cells = matrix?.polymerBondToCells.get(this.polymerBond); @@ -132,8 +133,6 @@ export class SnakeModeHydrogenBondRenderer extends BaseRenderer { } else { this.appendBondGraph(rootElement); } - - return this.bodyElement; } // TODO: Specify the types. diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 0c5cc8c283..f3d9a09fea 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -111,8 +111,9 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.isMonomersOnSameHorizontalLine(); } - // TODO: Specify the types. - public appendBond(rootElement) { + public appendBond( + rootElement: D3SvgElementSelection, + ): void { const editor = CoreEditor.provideEditorInstance(); const matrix = editor.drawingEntitiesManager.canvasMatrix; const cells = matrix?.polymerBondToCells.get(this.polymerBond); @@ -128,8 +129,6 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { } else { this.appendBondGraph(rootElement); } - - return this.bodyElement; } // TODO: Specify the types. From 0dd84e684c286e4387078c7d05b56a39105e2ee3 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Thu, 5 Dec 2024 20:55:33 +0300 Subject: [PATCH 14/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20/=20Bugfix?= =?UTF-8?q?=20(SnakeModePolymerBondRenderer):=20Recover=20`sideConnectionB?= =?UTF-8?q?ondTurnPoint`=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SideChainConnectionBondRenderer.ts | 6 ++++++ .../SnakeModePolymerBondRenderer.ts | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts index 48ca42d68a..4c495eb09c 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts @@ -30,6 +30,7 @@ interface AppendSideConnectionBondParameter { interface AppendSideConnectionBondResult { readonly appendPathToElement: AppendPathToElementFunction; readonly pathDAttributeValue: string; + readonly sideConnectionBondTurnPointUpdated: number; } const BOND_END_LENGTH = 15; @@ -46,6 +47,8 @@ export class SideChainConnectionBondRenderer { scaledPosition, sideConnectionBondTurnPoint, }: AppendSideConnectionBondParameter): AppendSideConnectionBondResult { + let sideConnectionBondTurnPointUpdated = sideConnectionBondTurnPoint ?? 0; + const firstCell = cells[0]; const firstCellConnection = firstCell.connections.find((connection) => { return connection.polymerBond === polymerBond; @@ -120,6 +123,7 @@ export class SideChainConnectionBondRenderer { }); pathDAttributeValue += result.pathPart; sideConnectionBondTurnPoint = result.sideConnectionBondTurnPoint; + sideConnectionBondTurnPointUpdated = result.sideConnectionBondTurnPoint; } let maxHorizontalOffset = 0; @@ -204,6 +208,7 @@ export class SideChainConnectionBondRenderer { }); pathDAttributeValue += result.pathPart; sideConnectionBondTurnPoint = result.sideConnectionBondTurnPoint; + sideConnectionBondTurnPointUpdated = result.sideConnectionBondTurnPoint; } previousCell = cell; previousConnection = cellConnection; @@ -230,6 +235,7 @@ export class SideChainConnectionBondRenderer { return { appendPathToElement, pathDAttributeValue, + sideConnectionBondTurnPointUpdated, }; } diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index f3d9a09fea..be3084a054 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -154,13 +154,18 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { ): D3SvgElementSelection { const sideChainConnectionBondRenderer = new SideChainConnectionBondRenderer(); - const { appendPathToElement, pathDAttributeValue } = - sideChainConnectionBondRenderer.appendSideConnectionBond({ - cells, - polymerBond: this.polymerBond, - scaledPosition: this.scaledPosition, - sideConnectionBondTurnPoint: this.sideConnectionBondTurnPoint, - }); + const { + appendPathToElement, + pathDAttributeValue, + sideConnectionBondTurnPointUpdated, + } = sideChainConnectionBondRenderer.appendSideConnectionBond({ + cells, + polymerBond: this.polymerBond, + scaledPosition: this.scaledPosition, + sideConnectionBondTurnPoint: this.sideConnectionBondTurnPoint, + }); + + this.sideConnectionBondTurnPoint = sideConnectionBondTurnPointUpdated; this.bodyElement = appendPathToElement( rootElement, From 37f01283dcf2ebbd681fcede7d572e72ce32b510 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 01:15:03 +0300 Subject: [PATCH 15/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SideChain?= =?UTF-8?q?ConnectionBondRenderer):=20Create=20`calculateEndOfPathPart(1)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SideChainConnectionBondRenderer.ts | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts index 4c495eb09c..aac6fc5e53 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts @@ -1,7 +1,4 @@ -import { - BaseMonomerRenderer, - BaseSequenceItemRenderer, -} from 'application/render'; +import { BaseMonomerRenderer } from 'application/render'; import { D3SvgElementSelection } from 'application/render/types'; import { BaseMonomer, PolymerBond, Vec2 } from 'domain/entities'; import { Cell } from 'domain/entities/canvas-matrix/Cell'; @@ -33,6 +30,15 @@ interface AppendSideConnectionBondResult { readonly sideConnectionBondTurnPointUpdated: number; } +interface CalculateEndOfPathPartParameter { + readonly horizontal: boolean; + readonly monomerSize: BaseMonomerRenderer['monomerSize']; + readonly scaledMonomerPosition: Vec2; + readonly sideConnectionBondTurnPoint: number; + readonly xOffset: number; + readonly yOffset: number; +} + const BOND_END_LENGTH = 15; const CELL_HEIGHT = 40; const SMOOTH_CORNER_SIZE = 5; @@ -239,6 +245,22 @@ export class SideChainConnectionBondRenderer { }; } + private calculateEndOfPathPart({ + horizontal, + monomerSize, + scaledMonomerPosition, + sideConnectionBondTurnPoint, + xOffset, + yOffset, + }: CalculateEndOfPathPartParameter): number { + if (horizontal && sideConnectionBondTurnPoint) { + return sideConnectionBondTurnPoint; + } + return horizontal + ? scaledMonomerPosition.x + monomerSize.width / 2 + xOffset + : scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; + } + // TODO: Specify the types. private drawPartOfSideConnection({ cell, @@ -274,17 +296,16 @@ export class SideChainConnectionBondRenderer { 0, ); - let endOfPathPart: number; - if (horizontal && sideConnectionBondTurnPoint) { - endOfPathPart = sideConnectionBondTurnPoint; - } else { - const { monomerSize, scaledMonomerPosition } = ( - cell.monomer as BaseMonomer - ).renderer as BaseMonomerRenderer | BaseSequenceItemRenderer; - endOfPathPart = horizontal - ? scaledMonomerPosition.x + monomerSize.width / 2 + xOffset - : scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; - } + const { monomerSize, scaledMonomerPosition } = (cell.monomer as BaseMonomer) + .renderer as BaseMonomerRenderer; + let endOfPathPart = this.calculateEndOfPathPart({ + horizontal, + monomerSize, + scaledMonomerPosition, + sideConnectionBondTurnPoint, + xOffset, + yOffset, + }); const sideConnectionBondTurnPointInternal = endOfPathPart; From 1062c3e567774909a1809dbbfe41269b9a791042 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 01:17:51 +0300 Subject: [PATCH 16/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SideChain?= =?UTF-8?q?ConnectionBondRenderer):=20Create=20the=20interfaces=20for=20`d?= =?UTF-8?q?rawPartOfSideConnection(1)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SideChainConnectionBondRenderer.ts | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts index aac6fc5e53..3377c84061 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts @@ -39,6 +39,19 @@ interface CalculateEndOfPathPartParameter { readonly yOffset: number; } +interface DrawPartOfSideConnectionParameter { + readonly cell: Cell; + readonly connection: Connection; + readonly direction: ConnectionDirectionInDegrees; + readonly horizontal: boolean; + readonly sideConnectionBondTurnPoint: number; +} + +interface DrawPartOfSideConnectionResult { + readonly pathPart: string; + readonly sideConnectionBondTurnPoint: number; +} + const BOND_END_LENGTH = 15; const CELL_HEIGHT = 40; const SMOOTH_CORNER_SIZE = 5; @@ -261,23 +274,13 @@ export class SideChainConnectionBondRenderer { : scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; } - // TODO: Specify the types. private drawPartOfSideConnection({ cell, connection, direction, horizontal, sideConnectionBondTurnPoint, - }: { - readonly cell: Cell; - readonly connection: Connection; - readonly direction: ConnectionDirectionInDegrees; - readonly horizontal: boolean; - readonly sideConnectionBondTurnPoint: number; - }): { - readonly pathPart: string; - readonly sideConnectionBondTurnPoint: number; - } { + }: DrawPartOfSideConnectionParameter): DrawPartOfSideConnectionResult { const sin = Math.sin((direction * Math.PI) / 180); const cos = Math.cos((direction * Math.PI) / 180); const xOffset = (SNAKE_LAYOUT_CELL_WIDTH / 2) * cos; From a2cc652af3cf7076b1e385a9ea4c78e00032940f Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 17:50:40 +0300 Subject: [PATCH 17/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SideChain?= =?UTF-8?q?ConnectionBondRenderer):=20Specify=20the=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SideChainConnectionBondRenderer.ts | 34 ++++++++++++------- .../SnakeModeHydrogenBondRenderer.ts | 9 +++-- .../SnakeModePolymerBondRenderer.ts | 23 ++++++++----- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts index 3377c84061..4d6b06ef5b 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts @@ -9,10 +9,19 @@ import { } from 'domain/entities/canvas-matrix/Connection'; import { SNAKE_LAYOUT_CELL_WIDTH } from 'domain/entities/DrawingEntitiesManager'; -type AppendPathToElementFunction = ( - element: D3SvgElementSelection, +type RendererBodyElement = D3SvgElementSelection< + SVGPathElement, + ThisType +>; +type RendererRootElement = D3SvgElementSelection< + SVGGElement, + ThisType +>; + +type AppendPathToElementFunction = ( + element: RendererRootElement, cssClassForPath: string, -) => D3SvgElementSelection; +) => RendererBodyElement; interface AppendSideConnectionBondParameter { readonly cells: readonly Cell[]; @@ -24,8 +33,8 @@ interface AppendSideConnectionBondParameter { readonly sideConnectionBondTurnPoint?: number; } -interface AppendSideConnectionBondResult { - readonly appendPathToElement: AppendPathToElementFunction; +interface AppendSideConnectionBondResult { + readonly appendPathToElement: AppendPathToElementFunction; readonly pathDAttributeValue: string; readonly sideConnectionBondTurnPointUpdated: number; } @@ -60,12 +69,12 @@ const SMOOTH_CORNER_SIZE = 5; // - a class with static methods. // - a set of functions. export class SideChainConnectionBondRenderer { - public appendSideConnectionBond({ + public appendSideConnectionBond({ cells, polymerBond, scaledPosition, sideConnectionBondTurnPoint, - }: AppendSideConnectionBondParameter): AppendSideConnectionBondResult { + }: AppendSideConnectionBondParameter): AppendSideConnectionBondResult { let sideConnectionBondTurnPointUpdated = sideConnectionBondTurnPoint ?? 0; const firstCell = cells[0]; @@ -235,12 +244,13 @@ export class SideChainConnectionBondRenderer { pathDAttributeValue += `L ${endPosition.x},${endPosition.y} `; - const appendPathToElement: AppendPathToElementFunction = ( - element: D3SvgElementSelection, + const appendPathToElement: AppendPathToElementFunction = < + ThisType, + >( + element: RendererRootElement, cssClassForPath: string, - ): D3SvgElementSelection => { - const pathElement: D3SvgElementSelection = - element.append('path'); + ): RendererBodyElement => { + const pathElement: RendererBodyElement = element.append('path'); return pathElement .attr('class', cssClassForPath) .attr('d', pathDAttributeValue) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts index 1664bcda06..5192e2fd1e 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeHydrogenBondRenderer.ts @@ -112,7 +112,7 @@ export class SnakeModeHydrogenBondRenderer extends BaseRenderer { } public appendBond( - rootElement: D3SvgElementSelection, + rootElement: D3SvgElementSelection, ): void { const editor = CoreEditor.provideEditorInstance(); const matrix = editor.drawingEntitiesManager.canvasMatrix; @@ -738,7 +738,12 @@ export class SnakeModeHydrogenBondRenderer extends BaseRenderer { this.sideConnectionBondTurnPoint = undefined; } this.rootElement = this.rootElement || this.appendRootElement(); - this.appendBond(this.rootElement); + this.appendBond( + this.rootElement as D3SvgElementSelection< + SVGGElement, + unknown + > as D3SvgElementSelection, + ); this.appendHoverAreaElement(); this.drawSelection(); } diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index be3084a054..583ed9e0b8 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -38,8 +38,8 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { // TODO: Specify the type of `selectionElement`. // private selectionElement?: - // | D3SvgElementSelection - // | D3SvgElementSelection; + // | D3SvgElementSelection + // | D3SvgElementSelection; private selectionElement; private editorEvents: typeof editorEvents; @@ -112,7 +112,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { } public appendBond( - rootElement: D3SvgElementSelection, + rootElement: D3SvgElementSelection, ): void { const editor = CoreEditor.provideEditorInstance(); const matrix = editor.drawingEntitiesManager.canvasMatrix; @@ -149,7 +149,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { } private appendSideConnectionBond( - rootElement: D3SvgElementSelection, + rootElement: D3SvgElementSelection, cells: Cell[], ): D3SvgElementSelection { const sideChainConnectionBondRenderer = @@ -158,7 +158,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { appendPathToElement, pathDAttributeValue, sideConnectionBondTurnPointUpdated, - } = sideChainConnectionBondRenderer.appendSideConnectionBond({ + } = sideChainConnectionBondRenderer.appendSideConnectionBond({ cells, polymerBond: this.polymerBond, scaledPosition: this.scaledPosition, @@ -544,7 +544,12 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.sideConnectionBondTurnPoint = undefined; } this.rootElement = this.rootElement || this.appendRootElement(); - this.appendBond(this.rootElement); + this.appendBond( + this.rootElement as D3SvgElementSelection< + SVGGElement, + unknown + > as D3SvgElementSelection, + ); this.appendHoverAreaElement(); this.drawSelection(); } @@ -716,7 +721,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { Array.from(allSideConnectionBondsBodyElements).forEach( (bondBodyElement) => { - bondBodyElement.setAttribute('stroke', '#C0E2E6'); + bondBodyElement.setAttribute('stroke', '#c0e2e6'); }, ); } @@ -747,7 +752,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { bondBodyElement.setAttribute( 'stroke', - renderer.polymerBond.isSideChainConnection ? '#43B5C0' : '#333333', + renderer.polymerBond.isSideChainConnection ? '#43b5c0' : '#333333', ); }, ); @@ -756,7 +761,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.bodyElement .attr( 'stroke', - this.polymerBond.isSideChainConnection && true ? '#43B5C0' : '#333333', + this.polymerBond.isSideChainConnection ? '#43b5c0' : '#333333', ) .attr('pointer-events', 'stroke'); From 79de1bcc7a249897c92a7dbec1ae8e2d6b8b016e Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 17:52:59 +0300 Subject: [PATCH 18/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?SideChainBondRenderer):=20Rename=20the=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PolymerBondRenderer/SnakeModePolymerBondRenderer.ts | 4 ++-- ...ctionBondRenderer.ts => SnakeModeSideChainBondRenderer.ts} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/{SideChainConnectionBondRenderer.ts => SnakeModeSideChainBondRenderer.ts} (99%) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 583ed9e0b8..78d3f99785 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -1,7 +1,7 @@ import { editorEvents } from 'application/editor/editorEvents'; import { CoreEditor } from 'application/editor/internal'; import { Coordinates } from 'application/editor/shared/coordinates'; -import { SideChainConnectionBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer'; +import { SnakeModeSideChainBondRenderer } from 'application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer'; import { D3SvgElementSelection } from 'application/render/types'; import assert from 'assert'; import { Vec2 } from 'domain/entities'; @@ -153,7 +153,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { cells: Cell[], ): D3SvgElementSelection { const sideChainConnectionBondRenderer = - new SideChainConnectionBondRenderer(); + new SnakeModeSideChainBondRenderer(); const { appendPathToElement, pathDAttributeValue, diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts similarity index 99% rename from packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts rename to packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts index 4d6b06ef5b..b933ded331 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SideChainConnectionBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts @@ -68,7 +68,7 @@ const SMOOTH_CORNER_SIZE = 5; // TODO: Can be: // - a class with static methods. // - a set of functions. -export class SideChainConnectionBondRenderer { +export class SnakeModeSideChainBondRenderer { public appendSideConnectionBond({ cells, polymerBond, From bba5bcd25fa2fda3c282ce9e27bc13062ed20c5c Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 17:55:18 +0300 Subject: [PATCH 19/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?PolymerBondRenderer):=20Specify=20the=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PolymerBondRenderer/SnakeModePolymerBondRenderer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 78d3f99785..8a3c6ec024 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -554,10 +554,6 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.drawSelection(); } - private get isSideConnectionBondDrawn() { - return this.polymerBond.isSideChainConnection && this.path; - } - public drawSelection(): void { if (this.polymerBond.selected) { this.selectionElement?.remove(); @@ -795,4 +791,8 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.editorEvents.mouseLeaveMonomer.dispatch(); } } + + private get isSideConnectionBondDrawn(): boolean { + return this.polymerBond.isSideChainConnection && !!this.path; + } } From 2d6a68ef877d2a1ca696d686d3f3e7ac52c5051b Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 18:09:27 +0300 Subject: [PATCH 20/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?SideChainBondRenderer):=20Rename=20the=20class=20(part=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PolymerBondRenderer/SnakeModePolymerBondRenderer.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 8a3c6ec024..85c8e69a3a 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -152,13 +152,12 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { rootElement: D3SvgElementSelection, cells: Cell[], ): D3SvgElementSelection { - const sideChainConnectionBondRenderer = - new SnakeModeSideChainBondRenderer(); + const snakeModeSideChainBondRenderer = new SnakeModeSideChainBondRenderer(); const { appendPathToElement, pathDAttributeValue, sideConnectionBondTurnPointUpdated, - } = sideChainConnectionBondRenderer.appendSideConnectionBond({ + } = snakeModeSideChainBondRenderer.appendSideConnectionBond({ cells, polymerBond: this.polymerBond, scaledPosition: this.scaledPosition, From cfe6f8405b6f131c2a89c2f307168da34fa3b297 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 18:19:10 +0300 Subject: [PATCH 21/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?SideChainBondRenderer):=20Create=20`appendPathToElement(1)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SnakeModePolymerBondRenderer.ts | 28 ++++----- .../SnakeModeSideChainBondRenderer.ts | 57 +++++++++---------- 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 85c8e69a3a..0d4bd114bd 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -153,23 +153,23 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { cells: Cell[], ): D3SvgElementSelection { const snakeModeSideChainBondRenderer = new SnakeModeSideChainBondRenderer(); - const { - appendPathToElement, - pathDAttributeValue, - sideConnectionBondTurnPointUpdated, - } = snakeModeSideChainBondRenderer.appendSideConnectionBond({ - cells, - polymerBond: this.polymerBond, - scaledPosition: this.scaledPosition, - sideConnectionBondTurnPoint: this.sideConnectionBondTurnPoint, - }); + const { pathDAttributeValue, sideConnectionBondTurnPointUpdated } = + snakeModeSideChainBondRenderer.appendSideConnectionBond({ + cells, + polymerBond: this.polymerBond, + scaledPosition: this.scaledPosition, + sideConnectionBondTurnPoint: this.sideConnectionBondTurnPoint, + }); this.sideConnectionBondTurnPoint = sideConnectionBondTurnPointUpdated; - this.bodyElement = appendPathToElement( - rootElement, - SIDE_CONNECTION_BODY_ELEMENT_CLASS, - ).data([this]); + this.bodyElement = snakeModeSideChainBondRenderer.appendPathToElement( + { + cssClassForPath: SIDE_CONNECTION_BODY_ELEMENT_CLASS, + element: rootElement, + pathDAttributeValue, + }, + ); this.path = pathDAttributeValue; diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts index b933ded331..132cdbdb29 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts @@ -9,19 +9,17 @@ import { } from 'domain/entities/canvas-matrix/Connection'; import { SNAKE_LAYOUT_CELL_WIDTH } from 'domain/entities/DrawingEntitiesManager'; -type RendererBodyElement = D3SvgElementSelection< +type D3SelectionSVGPath = D3SvgElementSelection< SVGPathElement, ThisType >; -type RendererRootElement = D3SvgElementSelection< - SVGGElement, - ThisType ->; +type D3SelectionSVGG = D3SvgElementSelection; -type AppendPathToElementFunction = ( - element: RendererRootElement, - cssClassForPath: string, -) => RendererBodyElement; +interface AppendPathToElementParameter { + pathDAttributeValue: string; + element: D3SelectionSVGG; + cssClassForPath: string; +} interface AppendSideConnectionBondParameter { readonly cells: readonly Cell[]; @@ -33,8 +31,7 @@ interface AppendSideConnectionBondParameter { readonly sideConnectionBondTurnPoint?: number; } -interface AppendSideConnectionBondResult { - readonly appendPathToElement: AppendPathToElementFunction; +interface AppendSideConnectionBondResult { readonly pathDAttributeValue: string; readonly sideConnectionBondTurnPointUpdated: number; } @@ -69,12 +66,28 @@ const SMOOTH_CORNER_SIZE = 5; // - a class with static methods. // - a set of functions. export class SnakeModeSideChainBondRenderer { - public appendSideConnectionBond({ + public appendPathToElement({ + cssClassForPath, + element, + pathDAttributeValue, + }: AppendPathToElementParameter): D3SelectionSVGPath { + const pathElement: D3SelectionSVGPath = element.append('path'); + return pathElement + .attr('class', cssClassForPath) + .attr('d', pathDAttributeValue) + .attr('pointer-events', 'stroke') + .attr('stroke', '#43b5c0') + .attr('stroke-dasharray', '0') + .attr('stroke-width', '1') + .attr('fill', 'none'); + } + + public appendSideConnectionBond({ cells, polymerBond, scaledPosition, sideConnectionBondTurnPoint, - }: AppendSideConnectionBondParameter): AppendSideConnectionBondResult { + }: AppendSideConnectionBondParameter): AppendSideConnectionBondResult { let sideConnectionBondTurnPointUpdated = sideConnectionBondTurnPoint ?? 0; const firstCell = cells[0]; @@ -244,25 +257,7 @@ export class SnakeModeSideChainBondRenderer { pathDAttributeValue += `L ${endPosition.x},${endPosition.y} `; - const appendPathToElement: AppendPathToElementFunction = < - ThisType, - >( - element: RendererRootElement, - cssClassForPath: string, - ): RendererBodyElement => { - const pathElement: RendererBodyElement = element.append('path'); - return pathElement - .attr('class', cssClassForPath) - .attr('d', pathDAttributeValue) - .attr('pointer-events', 'stroke') - .attr('stroke', '#43b5c0') - .attr('stroke-dasharray', '0') - .attr('stroke-width', 1) - .attr('fill', 'none'); - }; - return { - appendPathToElement, pathDAttributeValue, sideConnectionBondTurnPointUpdated, }; From b9c20c05057fe9ba801f9b7fd01324b65839b105 Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 18:21:20 +0300 Subject: [PATCH 22/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?SideChainBondRenderer):=20Delete=20the=20useless=20`.attr('stro?= =?UTF-8?q?ke-dasharray',=20'0')`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts index 132cdbdb29..59c2afbf90 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts @@ -77,7 +77,6 @@ export class SnakeModeSideChainBondRenderer { .attr('d', pathDAttributeValue) .attr('pointer-events', 'stroke') .attr('stroke', '#43b5c0') - .attr('stroke-dasharray', '0') .attr('stroke-width', '1') .attr('fill', 'none'); } From 75f294ac79dc45f0c48365181c3fae362048321d Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 18:31:35 +0300 Subject: [PATCH 23/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?PolymerBondRenderer):=20Create=20the=20constant=20for=20the=20c?= =?UTF-8?q?olors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SnakeModePolymerBondRenderer.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts index 0d4bd114bd..32532ce29a 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModePolymerBondRenderer.ts @@ -26,6 +26,14 @@ const DOUBLE_CORNER_LENGTH = CORNER_LENGTH * 2; const SIDE_CONNECTION_BODY_ELEMENT_CLASS = 'polymer-bond-body'; +// # Colors +const blue1Color = '#0097a8'; +const blue2Color = '#43b5c0'; +const blue3Color = '#c0e2e6'; +const grayColor = '#333333'; +const green1Color = '#57ff8f'; +const green2Color = '#ccffdd'; + // TODO: Need to split the class by three: // - `SnakeModeBackboneBondRenderer` (black “snake” line) // - `SnakeModeSideChainBondRenderer` (blue “snake” line) @@ -139,7 +147,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.bodyElement = rootElement .append('path') - .attr('stroke', this.polymerBond.finished ? '#333333' : '#0097A8') + .attr('stroke', this.polymerBond.finished ? grayColor : blue1Color) .attr('stroke-width', 1) .attr('class', 'selection-area') .attr('d', this.path) @@ -503,7 +511,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { public appendBondGraph(rootElement) { this.bodyElement = rootElement .append('line') - .attr('stroke', this.polymerBond.finished ? '#333333' : '#0097A8') + .attr('stroke', this.polymerBond.finished ? grayColor : blue1Color) .attr('stroke-width', 1) .attr('stroke-dasharray', '0') .attr('class', 'selection-area') @@ -562,7 +570,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { ) { this.selectionElement = this.rootElement ?.insert('path', ':first-child') - .attr('stroke', '#57FF8F') + .attr('stroke', green1Color) .attr('stroke-width', 2) .attr('fill-opacity', 0) .attr('d', this.path) @@ -570,7 +578,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { } else { this.selectionElement = this.rootElement ?.insert('line', ':first-child') - .attr('stroke', '#57FF8F') + .attr('stroke', green1Color) .attr('x1', this.scaledPosition.startPosition.x) .attr('y1', this.scaledPosition.startPosition.y) .attr('x2', this.scaledPosition.endPosition.x) @@ -716,15 +724,15 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { Array.from(allSideConnectionBondsBodyElements).forEach( (bondBodyElement) => { - bondBodyElement.setAttribute('stroke', '#c0e2e6'); + bondBodyElement.setAttribute('stroke', blue3Color); }, ); } - this.bodyElement.attr('stroke', '#0097A8').attr('pointer-events', 'none'); + this.bodyElement.attr('stroke', blue1Color).attr('pointer-events', 'none'); if (this.polymerBond.selected && this.selectionElement) { - this.selectionElement.attr('stroke', '#CCFFDD'); + this.selectionElement.attr('stroke', green2Color); } } @@ -747,7 +755,7 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { bondBodyElement.setAttribute( 'stroke', - renderer.polymerBond.isSideChainConnection ? '#43b5c0' : '#333333', + renderer.polymerBond.isSideChainConnection ? blue2Color : grayColor, ); }, ); @@ -756,12 +764,12 @@ export class SnakeModePolymerBondRenderer extends BaseRenderer { this.bodyElement .attr( 'stroke', - this.polymerBond.isSideChainConnection ? '#43b5c0' : '#333333', + this.polymerBond.isSideChainConnection ? blue2Color : grayColor, ) .attr('pointer-events', 'stroke'); if (this.polymerBond.selected && this.selectionElement) { - this.selectionElement.attr('stroke', '#57FF8F'); + this.selectionElement.attr('stroke', green1Color); } return this.hoverAreaElement.attr('stroke', 'transparent'); From 7130f51f63c5e67a6170543b2c6268b59a3235fc Mon Sep 17 00:00:00 2001 From: DmitriiP-EPAM Date: Fri, 6 Dec 2024 21:32:39 +0300 Subject: [PATCH 24/24] =?UTF-8?q?#5497=20=E2=80=93=20Refactor=20(SnakeMode?= =?UTF-8?q?SideChainBondRenderer):=20Rename=20`calculateSideConnectionBond?= =?UTF-8?q?TurnPoint(1)`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SnakeModeSideChainBondRenderer.ts | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts index 59c2afbf90..2b127fdb82 100644 --- a/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/PolymerBondRenderer/SnakeModeSideChainBondRenderer.ts @@ -16,9 +16,9 @@ type D3SelectionSVGPath = D3SvgElementSelection< type D3SelectionSVGG = D3SvgElementSelection; interface AppendPathToElementParameter { - pathDAttributeValue: string; - element: D3SelectionSVGG; - cssClassForPath: string; + readonly cssClassForPath: string; + readonly element: D3SelectionSVGG; + readonly pathDAttributeValue: string; } interface AppendSideConnectionBondParameter { @@ -36,7 +36,7 @@ interface AppendSideConnectionBondResult { readonly sideConnectionBondTurnPointUpdated: number; } -interface CalculateEndOfPathPartParameter { +interface CalculateSideConnectionBondTurnPointParameter { readonly horizontal: boolean; readonly monomerSize: BaseMonomerRenderer['monomerSize']; readonly scaledMonomerPosition: Vec2; @@ -262,20 +262,21 @@ export class SnakeModeSideChainBondRenderer { }; } - private calculateEndOfPathPart({ + private calculateSideConnectionBondTurnPoint({ horizontal, monomerSize, scaledMonomerPosition, sideConnectionBondTurnPoint, xOffset, yOffset, - }: CalculateEndOfPathPartParameter): number { - if (horizontal && sideConnectionBondTurnPoint) { - return sideConnectionBondTurnPoint; + }: CalculateSideConnectionBondTurnPointParameter): number { + if (horizontal) { + if (sideConnectionBondTurnPoint) { + return sideConnectionBondTurnPoint; + } + return scaledMonomerPosition.x + monomerSize.width / 2 + xOffset; } - return horizontal - ? scaledMonomerPosition.x + monomerSize.width / 2 + xOffset - : scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; + return scaledMonomerPosition.y + monomerSize.height / 2 + yOffset; } private drawPartOfSideConnection({ @@ -305,16 +306,17 @@ export class SnakeModeSideChainBondRenderer { const { monomerSize, scaledMonomerPosition } = (cell.monomer as BaseMonomer) .renderer as BaseMonomerRenderer; - let endOfPathPart = this.calculateEndOfPathPart({ - horizontal, - monomerSize, - scaledMonomerPosition, - sideConnectionBondTurnPoint, - xOffset, - yOffset, - }); + const sideConnectionBondTurnPointInternal = + this.calculateSideConnectionBondTurnPoint({ + horizontal, + monomerSize, + scaledMonomerPosition, + sideConnectionBondTurnPoint, + xOffset, + yOffset, + }); - const sideConnectionBondTurnPointInternal = endOfPathPart; + let endOfPathPart = sideConnectionBondTurnPointInternal; if (horizontal) { endOfPathPart +=