Skip to content

Commit

Permalink
Merge branch 'master' into #1954-draw-selected-structure-at-mouse-cur…
Browse files Browse the repository at this point in the history
…sor-as-pasting-does
  • Loading branch information
Stanislav Permiakov authored and Stanislav Permiakov committed Jan 24, 2023
2 parents 52e87eb + 8a8b26d commit 4292006
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 43 deletions.
138 changes: 119 additions & 19 deletions packages/ketcher-core/src/application/editor/actions/rotate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,30 +63,66 @@ export function fromFlip(restruct, selection, dir, center) {
})

if (typeof isFragFound === 'number') {
return action // empty action
return flipPartOfStructure({
fids,
struct,
restruct,
dir,
action,
selection
})
}

Object.keys(fids).forEach((frag) => {
const fragment = new Pile(fids[frag])

const bbox = struct.getCoordBoundingBox(fragment)
const calcCenter =
center ||
new Vec2((bbox.max.x + bbox.min.x) / 2, (bbox.max.y + bbox.min.y) / 2)
return flipStandaloneStructure({
fids,
struct,
restruct,
center,
dir,
action,
selection
})
}

fragment.forEach((aid) => {
const atom = struct.atoms.get(aid)
const d = flipItemByCenter(atom, calcCenter, dir)
action.addOp(new AtomMove(aid, d))
})
function getRotationPoint(struct, selection) {
const { bonds } = struct
const isAttachmentBond = ({ begin, end }) => {
const isBondStartsInSelectionAndEndsOutside =
selection.atoms.includes(begin) && !selection.atoms.includes(end)
const isBondEndsInSelectionAndStartsOutside =
selection.atoms.includes(end) && !selection.atoms.includes(begin)
return (
isBondStartsInSelectionAndEndsOutside ||
isBondEndsInSelectionAndStartsOutside
)
}
const isSelectedAtom = (atomId) => selection.atoms.includes(atomId)
const getAttachmentBond = () => {
for (const [bondId, bond] of bonds.entries()) {
if (isAttachmentBond(bond)) {
return [bondId, bond]
}
}
return [null, null]
}
const getRotationPointAtomId = (attachmentBondId, attachmentBond) => {
if (selection.bonds.includes(attachmentBondId)) {
return [attachmentBond.begin, attachmentBond.end].find(
(atomId) => !isSelectedAtom(atomId)
)
}
return [attachmentBond.begin, attachmentBond.end].find(isSelectedAtom)
}

const sgroups = getRelSgroupsBySelection(restruct, Array.from(fragment))
sgroups.forEach((sg) => {
const d = flipItemByCenter(sg, calcCenter, dir)
action.addOp(new SGroupDataMove(sg.id, d))
})
})
const [attachmentBondId, attachmentBond] = getAttachmentBond()
const rotationPointAtomId = getRotationPointAtomId(
attachmentBondId,
attachmentBond
)
return struct.atoms.get(rotationPointAtomId).pp
}

function flipBonds(selection, struct, action) {
if (selection.bonds) {
selection.bonds.forEach((bid) => {
const bond = struct.bonds.get(bid)
Expand All @@ -105,6 +141,70 @@ export function fromFlip(restruct, selection, dir, center) {
}
})
}
}

function flipPartOfStructure({
fids,
struct,
restruct,
dir,
action,
selection
}) {
const rotationPoint = getRotationPoint(struct, selection)

Object.keys(fids).forEach((frag) => {
const fragment = new Pile(fids[frag])

fragment.forEach((aid) => {
const atom = struct.atoms.get(aid)
const d = flipItemByCenter(atom, rotationPoint, dir)
action.addOp(new AtomMove(aid, d))
})

const sgroups = getRelSgroupsBySelection(restruct, Array.from(fragment))
sgroups.forEach((sg) => {
const d = flipItemByCenter(sg, rotationPoint, dir)
action.addOp(new SGroupDataMove(sg.id, d))
})
})

flipBonds(selection, struct, action)

return action.perform(restruct)
}

function flipStandaloneStructure({
fids,
struct,
restruct,
center,
dir,
action,
selection
}) {
Object.keys(fids).forEach((frag) => {
const fragment = new Pile(fids[frag])

const bbox = struct.getCoordBoundingBox(fragment)
const calcCenter =
center ||
new Vec2((bbox.max.x + bbox.min.x) / 2, (bbox.max.y + bbox.min.y) / 2)

fragment.forEach((aid) => {
const atom = struct.atoms.get(aid)
const d = flipItemByCenter(atom, calcCenter, dir)
action.addOp(new AtomMove(aid, d))
})

const sgroups = getRelSgroupsBySelection(restruct, Array.from(fragment))
sgroups.forEach((sg) => {
const d = flipItemByCenter(sg, calcCenter, dir)
action.addOp(new SGroupDataMove(sg.id, d))
})
})

flipBonds(selection, struct, action)

return action.perform(restruct)
}
Expand Down
14 changes: 12 additions & 2 deletions packages/ketcher-core/src/application/render/raphaelRender.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,12 @@ Render.prototype.update = function (force = false, viewSz = null) {
.transform(Scale.obj2scaled, this.options)
.translate(this.options.offset || new Vec2())

if (!this.options.autoScale) {
if (this.options.downScale) {
this.ctab.molecule.rescale()
}

const isAutoScale = this.options.autoScale || this.options.downScale
if (!isAutoScale) {
const ext = Vec2.UNIT.scaled(sf)
const eb = bb.sz().length() > 0 ? bb.extend(ext, ext) : bb
const vb = new Box2Abs(
Expand Down Expand Up @@ -237,7 +242,12 @@ Render.prototype.update = function (force = false, viewSz = null) {
let rescale =
this.options.rescaleAmount ||
Math.max(sz1.x / (csz.x - 2 * marg), sz1.y / (csz.y - 2 * marg))
if (this.options.maxBondLength / rescale > 1.0) rescale = 1.0

const isForceDownscale = this.options.downScale && rescale < 1
const isBondsLengthFit = this.options.maxBondLength / rescale > 1
if (isBondsLengthFit || isForceDownscale) {
rescale = 1
}
const sz2 = sz1.add(mv.scaled(2 * rescale))
/* eslint-disable no-mixed-operators */
this.paper.setViewBox(
Expand Down
18 changes: 12 additions & 6 deletions packages/ketcher-core/src/application/render/restruct/resgroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,22 @@ class ReSGroup extends ReObject {
}

getTextHighlightDimensions(padding = 0) {
let startX = 0
let startY = 0
let width = 0
let height = 0
const sGroupItem = this.item
const [firstAtomId] = sGroupItem.atoms
const sGroupAtom = this.render.ctab.atoms.get(firstAtomId)
const [sGroupAtomSVGElement] = sGroupAtom.visel.paths
const atomTextBoundingBox = sGroupAtomSVGElement.getBBox()
const { x, y, x2, y2 } = atomTextBoundingBox
const startX = x - this.render.options.offset.x - padding
const startY = y - this.render.options.offset.y - padding
const width = x2 - x + padding * 2
const height = y2 - y + padding * 2
if (sGroupAtomSVGElement) {
const atomTextBoundingBox = sGroupAtomSVGElement.getBBox()
const { x, y, x2, y2 } = atomTextBoundingBox
startX = x - this.render.options.offset.x - padding
startY = y - this.render.options.offset.y - padding
width = x2 - x + padding * 2
height = y2 - y + padding * 2
}

return { startX, startY, width, height }
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ketcher-react/src/script/editor/tool/sgroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ class SGroupTool {
}

// TODO: handle click on an existing group?
if (id !== null || (selection && selection.atoms)) {
if (id !== null || selection?.atoms?.length) {
this.editor.selection(selection)
SGroupTool.sgroupDialog(this.editor, id, this.type)
}
Expand Down
51 changes: 51 additions & 0 deletions packages/ketcher-react/src/script/ui/action/flips.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Bond, ReBond, ReStruct } from 'ketcher-core'

export function isFlipDisabled(editor): boolean {
const selection: { atoms: number[]; bonds: number[] } = editor.selection()
const restruct: ReStruct = editor.render.ctab

if (!selection) {
return false
}

const { bonds = [], atoms = [] } = selection
const isAttachmentBond = ({ begin, end }: Bond): boolean =>
(selection.atoms.includes(begin) && !selection.atoms.includes(end)) ||
(selection.atoms.includes(end) && !selection.atoms.includes(begin))
const getBondIdsForAtom = (atomId: number) => {
const result: number[] = []
for (const [bondId, bond] of restruct.bonds.entries()) {
if (bond.b.begin === atomId || bond.b.end === atomId) {
result.push(bondId)
}
}
return result
}
const getBondIdsForAttachmentAtoms = () => {
const result: number[] = []
for (const atomId of atoms) {
const atomBondIds = getBondIdsForAtom(atomId)
for (const atomBondId of atomBondIds) {
const bond: ReBond | undefined = restruct.bonds.get(atomBondId)
if (bond && isAttachmentBond(bond.b)) {
result.push(atomBondId)
}
}
}
return result
}
const getAmountOfAttachmentBonds = (): number => {
let amountOfAttachmentBonds = 0
const totalBondIds = new Set([...bonds, ...getBondIdsForAttachmentAtoms()])
for (const bondId of totalBondIds) {
const bond: ReBond | undefined = restruct.bonds.get(bondId)
if (bond && isAttachmentBond(bond.b)) {
amountOfAttachmentBonds++
}
}

return amountOfAttachmentBonds
}

return getAmountOfAttachmentBonds() > 1
}
3 changes: 3 additions & 0 deletions packages/ketcher-react/src/script/ui/action/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { RxnArrowMode, SimpleObjectMode, findStereoAtoms } from 'ketcher-core'
import { bond as bondSchema } from '../data/schema/struct-schema'
import isHidden from './isHidden'
import { toBondType } from '../data/convert/structconv'
import { isFlipDisabled } from './flips'

const toolActions = {
hand: {
Expand Down Expand Up @@ -91,12 +92,14 @@ const toolActions = {
shortcut: 'Alt+h',
title: 'Horizontal Flip',
action: { tool: 'rotate', opts: 'horizontal' },
disabled: isFlipDisabled,
hidden: (options) => isHidden(options, 'transform-flip-h')
},
'transform-flip-v': {
shortcut: 'Alt+v',
title: 'Vertical Flip',
action: { tool: 'rotate', opts: 'vertical' },
disabled: isFlipDisabled,
hidden: (options) => isHidden(options, 'transform-flip-v')
},
sgroup: {
Expand Down
14 changes: 7 additions & 7 deletions packages/ketcher-react/src/script/ui/component/form/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function TextArea({ schema, value, onChange, ...rest }) {

TextArea.val = (ev) => ev.target.value

function CheckBox({ schema, value = '', onChange, ...rest }) {
function CheckBox({ schema, value = '', onChange, innerRef, ...rest }) {
return (
<div className={classes.fieldSetItem}>
<input
Expand Down Expand Up @@ -331,6 +331,10 @@ function componentMap(props: Props) {
return type === 'radio' ? FieldSet : Select
}

const AnyComponentWithRef = React.forwardRef(
({ Component, ...props }: any, ref) => <Component {...props} innerRef={ref} />
)

class Input extends PureComponent<
Props & { innerRef: React.Ref<HTMLInputElement> }
> {
Expand All @@ -351,14 +355,10 @@ class Input extends PureComponent<

render() {
const { children, onChange, ...restProps } = this.props
const Component = this.component

const ComponentWithRef = React.forwardRef((props, ref) => (
<Component {...props} innerRef={ref} />
))

return (
<ComponentWithRef
<AnyComponentWithRef
Component={this.component}
ref={this.props.innerRef}
{...this.ctrl}
{...restProps}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ interface TemplateLibProps {
mode: string
tab: number
saltsAndSolvents: Template[]
renderOptions?: any
}

interface TemplateLibCallProps {
Expand Down Expand Up @@ -283,6 +284,7 @@ const TemplateDialog: FC<Props> = (props) => {
selected={props.selected}
onDelete={props.onDelete}
onAttach={props.onAttach}
renderOptions={props.renderOptions}
/>
</AccordionDetails>
</Accordion>
Expand All @@ -303,6 +305,7 @@ const TemplateDialog: FC<Props> = (props) => {
templates={filteredFG}
onSelect={(templ) => select(templ)}
selected={props.selected}
renderOptions={props.renderOptions}
/>
</div>
) : (
Expand All @@ -319,6 +322,7 @@ const TemplateDialog: FC<Props> = (props) => {
templates={filteredSaltsAndSolvents}
onSelect={(templ) => select(templ)}
selected={props.selected}
renderOptions={props.renderOptions}
/>
</div>
) : (
Expand Down Expand Up @@ -350,6 +354,7 @@ export default connect(
(store) => ({
...omit(['attach'], (store as any).templates),
initialTab: (store as any).modal?.prop?.tab || TemplateTabs.TemplateLibrary,
renderOptions: (store as any).editor?.render?.options,
functionalGroups: functionalGroupsSelector(store),
saltsAndSolvents: saltsAndSolventsSelector(store)
}),
Expand Down
Loading

0 comments on commit 4292006

Please sign in to comment.