export function fromPaste()

in packages/ketcher-core/src/application/editor/actions/paste.ts [54:253]


export function fromPaste(
  restruct,
  pstruct,
  point,
  angle = 0,
  isPreview = false,
): [Action, { atoms: number[]; bonds: number[] }, CreatedItems] {
  const xy0 = getStructCenter(pstruct);
  const offset = Vec2.diff(point, xy0);

  const action = new Action();

  const aidMap = new Map();
  const fridMap = new Map();

  const pasteItems = {
    // only atoms and bonds now
    atoms: [] as number[],
    bonds: [] as number[],
  };

  const items: CreatedItems = {
    atoms: [],
    bonds: [],
    rxnArrows: [],
    rxnPluses: [],
    texts: [],
    images: [],
    simpleObjects: [],
    multitailArrows: [],
  };

  pstruct.atoms.forEach((atom, aid) => {
    if (!fridMap.has(atom.fragment) && !pstruct.isAtomFromMacromolecule(aid)) {
      fridMap.set(
        atom.fragment,
        (
          action.addOp(
            new FragmentAdd(null, atom.fragment.properties).perform(restruct),
          ) as FragmentAdd
        ).frid,
      );
    }

    const tmpAtom = Object.assign(atom.clone(), {
      fragment: fridMap.get(atom.fragment),
    });
    const operation = new AtomAdd(
      tmpAtom,
      Vec2.diff(atom.pp, xy0).rotate(angle).add(point),
    ).perform(restruct) as AtomAdd;
    action.addOp(operation);
    aidMap.set(aid, operation.data.aid);

    pasteItems.atoms.push(operation.data.aid);
    items.atoms.push(operation.data.aid);

    action.mergeWith(
      fromRGroupAttachmentPointAddition(
        restruct,
        tmpAtom.attachmentPoints,
        operation.data.aid,
      ),
    );
  });

  pstruct.frags.forEach((frag, frid) => {
    if (!frag) return;
    if (frag.properties) {
      action.addOp(
        new FragmentSetProperties(fridMap.get(frid), frag.properties).perform(
          restruct,
        ),
      );
    }
    frag.stereoAtoms.forEach((aid) =>
      action.addOp(
        new FragmentAddStereoAtom(fridMap.get(frid), aidMap.get(aid)).perform(
          restruct,
        ),
      ),
    );
  });

  pstruct.bonds.forEach((bond) => {
    const operation = new BondAdd(
      aidMap.get(bond.begin),
      aidMap.get(bond.end),
      bond,
      false,
    ).perform(restruct) as BondAdd;
    action.addOp(operation);

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

  pstruct.sgroups.forEach((sg: SGroup) => {
    const newsgid = restruct.molecule.sgroups.newId();
    const sgAtoms = sg.atoms.map((aid) => aidMap.get(aid));
    const attachmentPoints = sg.cloneAttachmentPoints(aidMap);
    if (
      sg.isNotContractible(pstruct) &&
      !(sg instanceof MonomerMicromolecule)
    ) {
      sg.setAttr('expanded', true);
    }
    const sgAction = fromSgroupAddition(
      restruct,
      sg.type,
      sgAtoms,
      sg.data,
      newsgid,
      attachmentPoints,
      sg.pp ? sg.pp.add(offset) : null,
      sg.type === 'SUP' ? sg.isExpanded() : null,
      sg.data.name,
      sg,
    );
    sgAction.operations.reverse().forEach((oper) => {
      action.addOp(oper);
    });
  });

  pasteItems.atoms.forEach((aid) => {
    action.addOp(new CalcImplicitH([aid]).perform(restruct));
    new AtomAttr(aid, 'isPreview', isPreview).perform(restruct);
  });

  pstruct.rxnArrows.forEach((rxnArrow) => {
    const operation = new RxnArrowAdd(
      rxnArrow.pos.map((p) => p.add(offset)),
      rxnArrow.mode,
    ).perform(restruct);
    action.addOp(operation);
    items.rxnArrows.push(operation.data.id);
  });

  pstruct.rxnPluses.forEach((plus) => {
    const operation = new RxnPlusAdd(plus.pp.add(offset)).perform(restruct);
    action.addOp(operation);
    items.rxnPluses.push(operation.data.plid);
  });

  pstruct.simpleObjects.forEach((simpleObject) => {
    const operation = new SimpleObjectAdd(
      simpleObject.pos.map((p) => p.add(offset)),
      simpleObject.mode,
    ).perform(restruct);
    action.addOp(operation);
    items.simpleObjects.push(operation.data.id);
  });

  pstruct.texts.forEach((text) => {
    const operation = new TextCreate(
      text.content,
      text.position.add(offset),
      text.pos.map((p) => p.add(offset)),
    ).perform(restruct);
    action.addOp(operation);
    items.texts.push(operation.data.id);
  });

  pstruct.images.forEach((image: Image) => {
    const clonedImage = image.clone();
    clonedImage.addPositionOffset(offset);
    const operation = new ImageUpsert(clonedImage).perform(restruct);
    action.addOp(operation);
    items.images.push(operation.data.id);
  });

  pstruct.multitailArrows.forEach((multitailArrow: MultitailArrow) => {
    const clonedMultitailArrow = multitailArrow.clone();
    clonedMultitailArrow.move(offset);
    const operation = new MultitailArrowUpsert(clonedMultitailArrow).perform(
      restruct,
    );
    action.addOp(operation);
    items.multitailArrows.push(operation.data.id);
  });

  pstruct.rgroups.forEach((rg, rgid) => {
    rg.frags.forEach((__frag, frid) => {
      action.addOp(
        new RGroupFragment(rgid, fridMap.get(frid)).perform(restruct),
      );
    });
    const ifThen = pstruct.rgroups.get(rgid).ifthen;
    const newRgId = pstruct.rgroups.get(ifThen) ? ifThen : 0;
    action
      .mergeWith(fromRGroupAttrs(restruct, rgid, rg.getAttrs()))
      .mergeWith(fromUpdateIfThen(restruct, newRgId, rg.ifthen));
  });

  action.operations.reverse();
  return [action, pasteItems, items];
}