From ce62f5ba1f514adb1d08c91bf5387a79857ab31c Mon Sep 17 00:00:00 2001 From: Daniil Sloboda Date: Mon, 1 Jul 2024 23:42:08 +0400 Subject: [PATCH 1/3] #4911 - insert image from ket to canvas * Loading image from ket file to canvas * Saving image from struct to ket * operations to move image during paste\replace --- .../application/editor/actions/fragment.ts | 7 ++ .../src/application/editor/actions/paste.ts | 9 +++ .../editor/operations/OperationType.ts | 3 + .../src/application/editor/operations/base.ts | 8 +-- .../application/editor/operations/index.ts | 1 + .../editor/operations/rasterImage/index.ts | 2 + .../operations/rasterImage/rasterImageMove.ts | 31 ++++++++ .../rasterImage/rasterImageUpsertDelete.ts | 70 +++++++++++++++++++ .../render/restruct/rerasterImage.ts | 53 ++++++++++++++ .../application/render/restruct/restruct.ts | 21 +++++- .../src/domain/entities/rasterImage.ts | 38 ++++++++++ .../src/domain/entities/struct.ts | 12 ++++ .../ket/fromKet/rasterImageToStruct.ts | 29 ++++++++ .../domain/serializers/ket/ketSerializer.ts | 10 +++ .../src/domain/serializers/ket/schema.json | 38 ++++++++++ .../domain/serializers/ket/toKet/prepare.ts | 13 ++++ .../serializers/ket/toKet/rasterImageToKet.ts | 25 +++++++ 17 files changed, 365 insertions(+), 5 deletions(-) create mode 100644 packages/ketcher-core/src/application/editor/operations/rasterImage/index.ts create mode 100644 packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts create mode 100644 packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts create mode 100644 packages/ketcher-core/src/application/render/restruct/rerasterImage.ts create mode 100644 packages/ketcher-core/src/domain/entities/rasterImage.ts create mode 100644 packages/ketcher-core/src/domain/serializers/ket/fromKet/rasterImageToStruct.ts create mode 100644 packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts diff --git a/packages/ketcher-core/src/application/editor/actions/fragment.ts b/packages/ketcher-core/src/application/editor/actions/fragment.ts index 8f90501376..4607f5afc1 100644 --- a/packages/ketcher-core/src/application/editor/actions/fragment.ts +++ b/packages/ketcher-core/src/application/editor/actions/fragment.ts @@ -28,6 +28,7 @@ import { SGroupDataMove, SimpleObjectMove, TextMove, + RasterImageMove, } from '../operations'; import { Pile, RGroup, Vec2 } from 'domain/entities'; import { fromRGroupFragment, fromUpdateIfThen } from './rgroup'; @@ -127,6 +128,12 @@ export function fromMultipleMove(restruct, lists, d: Vec2) { }); } + if (lists.rasterImages) { + lists.rasterImages.forEach((rasterImage) => { + action.addOp(new RasterImageMove(rasterImage, d)); + }); + } + return action.perform(restruct); } diff --git a/packages/ketcher-core/src/application/editor/actions/paste.ts b/packages/ketcher-core/src/application/editor/actions/paste.ts index c5c13de7ac..93c8dedbdf 100644 --- a/packages/ketcher-core/src/application/editor/actions/paste.ts +++ b/packages/ketcher-core/src/application/editor/actions/paste.ts @@ -28,6 +28,7 @@ import { FragmentSetProperties, BondAttr, AtomAttr, + RasterImageUpsert, } from '../operations'; import { fromRGroupAttrs, fromUpdateIfThen } from './rgroup'; @@ -36,6 +37,7 @@ import { SGroup, Struct, Vec2 } from 'domain/entities'; import { fromSgroupAddition } from './sgroup'; import { fromRGroupAttachmentPointAddition } from './rgroupAttachmentPoint'; import { MonomerMicromolecule } from 'domain/entities/monomerMicromolecule'; +import { RasterImage } from 'domain/entities/rasterImage'; export function fromPaste( restruct, @@ -188,6 +190,13 @@ export function fromPaste( ); }); + pstruct.rasterImages.forEach((rasterImage: RasterImage) => { + const clonedImage = rasterImage.clone(); + clonedImage.addPositionOffset(offset); + + action.addOp(new RasterImageUpsert(clonedImage).perform(restruct)); + }); + pstruct.rgroups.forEach((rg, rgid) => { rg.frags.forEach((__frag, frid) => { action.addOp( diff --git a/packages/ketcher-core/src/application/editor/operations/OperationType.ts b/packages/ketcher-core/src/application/editor/operations/OperationType.ts index f11e58545f..d5599a46bd 100644 --- a/packages/ketcher-core/src/application/editor/operations/OperationType.ts +++ b/packages/ketcher-core/src/application/editor/operations/OperationType.ts @@ -82,6 +82,9 @@ export const OperationType = Object.freeze({ DRAWING_ENTITY_SELECT: 'Select drawing entity', DRAWING_ENTITY_HOVER: 'Hover drawing entity', SHOW_POLYMER_BOND_INFORMATION: 'Show polymer bond information', + RASTER_IMAGE_UPSERT: 'Upsert raster image', + RASTER_IMAGE_DELETE: 'Delete raster image', + RASTER_IMAGE_MOVE: 'Move raster image', }); export enum OperationPriority { diff --git a/packages/ketcher-core/src/application/editor/operations/base.ts b/packages/ketcher-core/src/application/editor/operations/base.ts index 684379ae71..12e360bf98 100644 --- a/packages/ketcher-core/src/application/editor/operations/base.ts +++ b/packages/ketcher-core/src/application/editor/operations/base.ts @@ -127,16 +127,16 @@ class BaseOperation { protected static invalidateItem( restruct: ReStruct, - map, + mapName: keyof typeof ReStruct.maps, id: number, level?: any, ) { - if (map === 'atoms') { + if (mapName === 'atoms') { BaseOperation.invalidateAtom(restruct, id, level); return; } - if (map === 'bonds') { + if (mapName === 'bonds') { BaseOperation.invalidateBond(restruct, id); if (level > 0) { @@ -145,7 +145,7 @@ class BaseOperation { return; } - restruct.markItem(map, id, level); + restruct.markItem(mapName, id, level); } protected static invalidateEnhancedFlag( diff --git a/packages/ketcher-core/src/application/editor/operations/index.ts b/packages/ketcher-core/src/application/editor/operations/index.ts index 508cde5074..648f0df7f0 100644 --- a/packages/ketcher-core/src/application/editor/operations/index.ts +++ b/packages/ketcher-core/src/application/editor/operations/index.ts @@ -26,6 +26,7 @@ export * from './FragmentStereoFlag'; export * from './calcimplicitH'; export * from './LoopMove'; export * from './OperationType'; +export * from './rasterImage'; export * from './rgroup'; export * from './rgroupAttachmentPoint'; export * from './rxn'; diff --git a/packages/ketcher-core/src/application/editor/operations/rasterImage/index.ts b/packages/ketcher-core/src/application/editor/operations/rasterImage/index.ts new file mode 100644 index 0000000000..8ccb960c04 --- /dev/null +++ b/packages/ketcher-core/src/application/editor/operations/rasterImage/index.ts @@ -0,0 +1,2 @@ +export * from './rasterImageUpsertDelete'; +export * from './rasterImageMove'; diff --git a/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts new file mode 100644 index 0000000000..47b9d6f7be --- /dev/null +++ b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts @@ -0,0 +1,31 @@ +import { BaseOperation } from 'application/editor/operations/base'; +import { Vec2 } from 'domain/entities'; +import { OperationType } from 'application/editor'; +import { ReStruct } from 'application/render'; +import { Scale } from 'domain/helpers'; + +export class RasterImageMove extends BaseOperation { + constructor(private id: number, private difference: Vec2) { + super(OperationType.RASTER_IMAGE_MOVE); + } + + execute(reStruct: ReStruct) { + const renderItem = reStruct.rasterImages.get(this.id); + const item = reStruct.molecule.rasterImages.get(this.id); + + if (!item || !renderItem) { + return; + } + const differenceScaled = Scale.modelToCanvas( + this.difference, + reStruct.render.options, + ); + + renderItem.move(differenceScaled); + item.addPositionOffset(this.difference); + } + + invert(): BaseOperation { + return new RasterImageMove(this.id, this.difference.negated()); + } +} diff --git a/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts new file mode 100644 index 0000000000..c09ae15f2b --- /dev/null +++ b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts @@ -0,0 +1,70 @@ +/**************************************************************************** + * Copyright 2021 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { BaseOperation } from 'application/editor/operations/base'; +import { OperationType } from 'application/editor'; +import { RasterImage } from 'domain/entities/rasterImage'; +import { ReStruct } from 'application/render'; +import { ReRasterImage } from 'application/render/restruct/rerasterImage'; + +export class RasterImageUpsert extends BaseOperation { + constructor(private readonly rasterImage: RasterImage, private id?: number) { + super(OperationType.RASTER_IMAGE_UPSERT); + } + + execute(reStruct: ReStruct) { + const struct = reStruct.molecule; + + if (!this.id) { + this.id = struct.rasterImages.newId(); + } + const id = this.id!; + const item = this.rasterImage.clone(); + struct.rasterImages.set(id, item); + reStruct.rasterImages.set(id, new ReRasterImage(item)); + + BaseOperation.invalidateItem(reStruct, 'rasterImages', id, 1); + } + + invert(): BaseOperation { + return new RasterImageDelete(this.id!); + } +} + +export class RasterImageDelete extends BaseOperation { + private rasterImage?: RasterImage; + constructor(private id: number) { + super(OperationType.RASTER_IMAGE_DELETE); + } + + execute(reStruct: ReStruct) { + const reRasterImage = reStruct.rasterImages.get(this.id); + + if (!reRasterImage) { + return; + } + + reRasterImage.remove(); + reStruct.markItemRemoved(); + reStruct.rasterImages.delete(this.id); + reStruct.molecule.rasterImages.delete(this.id); + } + + invert(): BaseOperation { + return new RasterImageUpsert(this.rasterImage!, this.id); + } +} diff --git a/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts b/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts new file mode 100644 index 0000000000..a22373183c --- /dev/null +++ b/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts @@ -0,0 +1,53 @@ +import { ReObject, ReStruct } from 'application/render'; +import { RasterImage } from 'domain/entities/rasterImage'; +import { RenderOptions } from 'application/render/render.types'; +import { Scale } from 'domain/helpers'; +import { RaphaelElement } from 'raphael'; +import { Vec2 } from 'domain/entities'; + +export class ReRasterImage extends ReObject { + private element?: RaphaelElement; + + static isSelectable(): boolean { + return true; + } + + constructor(private item: RasterImage) { + super('image'); + } + + show(restruct: ReStruct, options: RenderOptions) { + if (this.element) { + return; + } + + const render = restruct.render; + const [topLeft, bottomRight] = this.item.position; + const scaledTopLeft = Scale.modelToCanvas(topLeft, options); + const scaledBottomRight = Scale.modelToCanvas(bottomRight, options); + const topLeftWithOffset = scaledTopLeft.add(options.offset); + + const width = scaledBottomRight.x - scaledTopLeft.x; + const height = scaledBottomRight.y - scaledTopLeft.y; + + this.element = render.paper.image( + this.item.bitmap, + topLeftWithOffset.x, + topLeftWithOffset.y, + width, + height, + ); + } + + remove() { + if (this.element) { + this.element.remove(); + } + } + + move(diff: Vec2) { + if (this.element) { + this.element.translate(diff.x, diff.y); + } + } +} diff --git a/packages/ketcher-core/src/application/render/restruct/restruct.ts b/packages/ketcher-core/src/application/render/restruct/restruct.ts index 70bbc2f825..b764c43c3a 100644 --- a/packages/ketcher-core/src/application/render/restruct/restruct.ts +++ b/packages/ketcher-core/src/application/render/restruct/restruct.ts @@ -41,9 +41,10 @@ import { Render } from '../raphaelRender'; import Visel from './visel'; import util from '../util'; import { ReRGroupAttachmentPoint } from './rergroupAttachmentPoint'; +import { ReRasterImage } from 'application/render/restruct/rerasterImage'; class ReStruct { - public static maps = { + public static readonly maps = { atoms: ReAtom, bonds: ReBond, rxnPluses: ReRxnPlus, @@ -57,6 +58,7 @@ class ReStruct { reloops: ReLoop, simpleObjects: ReSimpleObject, texts: ReText, + rasterImages: ReRasterImage, } as const; public render: Render; @@ -74,6 +76,7 @@ class ReStruct { public sgroupData: Map = new Map(); public enhancedFlags: Map = new Map(); private simpleObjects: Map = new Map(); + public rasterImages: Map = new Map(); public texts: Map = new Map(); private initialized = false; private layers: Array = []; @@ -81,6 +84,7 @@ class ReStruct { private ccFragmentType: Pool = new Pool(); private structChanged = false; + // TWIMC, Those maps are accessed via dynamic names, using static maps field + 'Changed' string private atomsChanged: Map = new Map(); private simpleObjectsChanged: Map = new Map(); private rxnArrowsChanged: Map = new Map(); @@ -88,6 +92,7 @@ class ReStruct { private enhancedFlagsChanged: Map = new Map(); private bondsChanged: Map = new Map(); private textsChanged: Map = new Map(); + private rasterImagesChanged: Map = new Map(); private snappingBonds: number[] = []; constructor( @@ -155,6 +160,9 @@ class ReStruct { this.sgroupData.set(id, new ReDataSGroupData(item)); } }); + molecule.rasterImages.forEach((item, id) => { + this.rasterImages.set(id, new ReRasterImage(item)); + }); } get visibleRGroupAttachmentPoints() { @@ -514,6 +522,7 @@ class ReStruct { this.showEnhancedFlags(); this.showSimpleObjects(); this.showTexts(); + this.showImages(); this.clearMarks(); return true; @@ -712,6 +721,16 @@ class ReStruct { }); } + showImages() { + const options = this.render.options; + this.rasterImagesChanged.forEach((_, id) => { + const image = this.rasterImages.get(id); + if (image) { + image.show(this, options); + } + }); + } + setSelection(selection?) { const atoms: { selected: boolean; sgroup: number }[] = []; diff --git a/packages/ketcher-core/src/domain/entities/rasterImage.ts b/packages/ketcher-core/src/domain/entities/rasterImage.ts new file mode 100644 index 0000000000..ad39742ca9 --- /dev/null +++ b/packages/ketcher-core/src/domain/entities/rasterImage.ts @@ -0,0 +1,38 @@ +/**************************************************************************** + * Copyright 2021 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +import { BaseMicromoleculeEntity } from 'domain/entities/BaseMicromoleculeEntity'; +import { Vec2 } from 'domain/entities/vec2'; + +type Position = [Vec2, Vec2]; + +export class RasterImage extends BaseMicromoleculeEntity { + constructor(public bitmap: string, public position: [Vec2, Vec2]) { + super(); + } + + clone(): RasterImage { + return new RasterImage(this.bitmap, this.position); + } + + addPositionOffset(offset: Vec2) { + this.position = this.position.map((item) => item.add(offset)) as Position; + } + + scaled(scale: number): void { + this.position = this.position.map((item) => item.scaled(scale)) as Position; + } +} diff --git a/packages/ketcher-core/src/domain/entities/struct.ts b/packages/ketcher-core/src/domain/entities/struct.ts index b173ce5593..a8b2e3e494 100644 --- a/packages/ketcher-core/src/domain/entities/struct.ts +++ b/packages/ketcher-core/src/domain/entities/struct.ts @@ -38,6 +38,7 @@ import { Highlight } from './highlight'; import { RGroupAttachmentPoint } from './rgroupAttachmentPoint'; import { MonomerMicromolecule } from 'domain/entities/monomerMicromolecule'; import { isNumber } from 'lodash'; +import { RasterImage } from './rasterImage'; export type Neighbor = { aid: number; @@ -73,6 +74,7 @@ export class Struct { abbreviation?: string; sGroupForest: SGroupForest; simpleObjects: Pool; + rasterImages: Pool; texts: Pool; functionalGroups: Pool; highlights: Pool; @@ -96,6 +98,7 @@ export class Struct { this.texts = new Pool(); this.functionalGroups = new Pool(); this.highlights = new Pool(); + this.rasterImages = new Pool(); } hasRxnProps(): boolean { @@ -211,12 +214,15 @@ export class Struct { textsSet?: Pile | null, rgroupAttachmentPointSet?: Pile | null, bidMapEntity?: Map | null, + rasterImagesSet?: Pile | null, ): Struct { atomSet = atomSet || new Pile(this.atoms.keys()); bondSet = bondSet || new Pile(this.bonds.keys()); simpleObjectsSet = simpleObjectsSet || new Pile(this.simpleObjects.keys()); textsSet = textsSet || new Pile(this.texts.keys()); + rasterImagesSet = + rasterImagesSet || new Pile(this.rasterImages.keys()); rgroupAttachmentPointSet = rgroupAttachmentPointSet || new Pile(this.rgroupAttachmentPoints.keys()); @@ -329,6 +335,10 @@ export class Struct { cp.texts.add(this.texts.get(id)!.clone()); }); + rasterImagesSet.forEach((id) => { + cp.rasterImages.add(this.rasterImages.get(id)!.clone()); + }); + rgroupAttachmentPointSet.forEach((id) => { const rgroupAttachmentPoint = this.rgroupAttachmentPoints.get(id); assert(rgroupAttachmentPoint != null); @@ -826,6 +836,8 @@ export class Struct { this.simpleObjects.forEach((simpleObjects) => { simpleObjects.pos = simpleObjects.pos.map((p) => p.scaled(scale)); }); + + this.rasterImages.forEach((rasterImage) => rasterImage.scaled(scale)); } rescale() { diff --git a/packages/ketcher-core/src/domain/serializers/ket/fromKet/rasterImageToStruct.ts b/packages/ketcher-core/src/domain/serializers/ket/fromKet/rasterImageToStruct.ts new file mode 100644 index 0000000000..bdb98172d2 --- /dev/null +++ b/packages/ketcher-core/src/domain/serializers/ket/fromKet/rasterImageToStruct.ts @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright 2021 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +import { Point, Struct, Vec2 } from 'domain/entities'; +import { getNodeWithInvertedYCoord } from '../helpers'; +import { RasterImage } from 'domain/entities/rasterImage'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function rasterImageToStruct(ketItem: any, struct: Struct): Struct { + const { bitmap, position } = getNodeWithInvertedYCoord(ketItem.data); + const vectorPosition = position.map((item: Point) => new Vec2(item)); + const rasterImage = new RasterImage(bitmap, vectorPosition); + rasterImage.setInitiallySelected(ketItem.selected); + struct.rasterImages.add(rasterImage); + return struct; +} diff --git a/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts b/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts index 3b066ad397..03bfa08a96 100644 --- a/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts +++ b/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts @@ -70,6 +70,8 @@ import { getAttachmentPointLabelWithBinaryShift } from 'domain/helpers/attachmen import { isNumber } from 'lodash'; import { MonomerItemType } from 'domain/types'; import { PolymerBond } from 'domain/entities/PolymerBond'; +import { rasterImageToKet } from 'domain/serializers/ket/toKet/rasterImageToKet'; +import { rasterImageToStruct } from 'domain/serializers/ket/fromKet/rasterImageToStruct'; function parseNode(node: any, struct: any) { const type = node.type; @@ -104,6 +106,10 @@ function parseNode(node: any, struct: any) { textToStruct(node, struct); break; } + case 'rasterImage': { + rasterImageToStruct(node, struct); + break; + } default: break; } @@ -174,6 +180,10 @@ export class KetSerializer implements Serializer { result.root.nodes.push(textToKet(item)); break; } + case 'rasterImage': { + result.root.nodes.push(rasterImageToKet(item)); + break; + } default: break; } diff --git a/packages/ketcher-core/src/domain/serializers/ket/schema.json b/packages/ketcher-core/src/domain/serializers/ket/schema.json index c01aa7f626..146d0f7e18 100644 --- a/packages/ketcher-core/src/domain/serializers/ket/schema.json +++ b/packages/ketcher-core/src/domain/serializers/ket/schema.json @@ -26,6 +26,9 @@ { "$ref": "#/definitions/plus" }, + { + "$ref": "#/definitions/rasterImage" + }, { "type": "object", "required": ["$ref"], @@ -777,6 +780,41 @@ "type": "boolean" } } + }, + "rasterImage": { + "type": "object", + "required": ["type", "data"], + "properties": { + "type": { + "const": "rasterImage" + }, + "data": { + "type": "object", + "required": ["bitmap", "position"], + "properties": { + "bitmap": { + "type": "string" + }, + "position": { + "type": "array", + "items": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "z": { + "type": "number" + } + } + } + } + } + } + } } } } diff --git a/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts b/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts index 3f41e6f2bb..0d4c0b8113 100644 --- a/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts +++ b/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts @@ -96,6 +96,19 @@ export function prepareStructForKet(struct: Struct) { }); }); + struct.rasterImages.forEach((image) => { + const center: Vec2 = Vec2.centre(image.position[0], image.position[1]); + ketNodes.push({ + type: 'rasterImage', + center, + data: { + bitmap: image.bitmap, + position: image.position, + }, + selected: image.getInitiallySelected(), + }); + }); + ketNodes.forEach((ketNode) => { if (ketNode.fragment) { const sgroups: SGroup[] = Array.from(ketNode.fragment.sgroups.values()); diff --git a/packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts b/packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts new file mode 100644 index 0000000000..cc2c53d1fd --- /dev/null +++ b/packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts @@ -0,0 +1,25 @@ +/**************************************************************************** + * Copyright 2021 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +import { getNodeWithInvertedYCoord } from '../helpers'; + +export function rasterImageToKet(rasterImageNode) { + return { + type: 'rasterImage', + data: getNodeWithInvertedYCoord(rasterImageNode.data), + selected: rasterImageNode.selected, + }; +} From 11708018145adb1408746c78d58860c6010dbaae Mon Sep 17 00:00:00 2001 From: Daniil Sloboda Date: Tue, 2 Jul 2024 17:53:13 +0400 Subject: [PATCH 2/3] #4911 - Fixing re rasterImage import --- .../src/application/render/restruct/rerasterImage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts b/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts index a22373183c..90a27c8ae2 100644 --- a/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts +++ b/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts @@ -1,4 +1,4 @@ -import { ReObject, ReStruct } from 'application/render'; +import { ReObject, ReStruct } from 'application/render/restruct'; import { RasterImage } from 'domain/entities/rasterImage'; import { RenderOptions } from 'application/render/render.types'; import { Scale } from 'domain/helpers'; From 4ec46957098f06b249eba1835cbbe7bcc5a824ba Mon Sep 17 00:00:00 2001 From: Daniil Sloboda Date: Wed, 3 Jul 2024 10:32:22 +0400 Subject: [PATCH 3/3] #4911 - Fixed PR comments: string const, show, variable naming --- .../editor/operations/rasterImage/rasterImageMove.ts | 12 ++++++------ .../rasterImage/rasterImageUpsertDelete.ts | 2 +- .../src/application/render/restruct/rerasterImage.ts | 2 +- .../ketcher-core/src/domain/entities/rasterImage.ts | 9 +++++++-- packages/ketcher-core/src/domain/entities/struct.ts | 4 +++- .../src/domain/serializers/ket/ketSerializer.ts | 5 +++-- .../src/domain/serializers/ket/schema.json | 3 --- .../src/domain/serializers/ket/toKet/prepare.ts | 3 ++- .../domain/serializers/ket/toKet/rasterImageToKet.ts | 3 ++- 9 files changed, 25 insertions(+), 18 deletions(-) diff --git a/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts index 47b9d6f7be..6f7f8c02fe 100644 --- a/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts +++ b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageMove.ts @@ -5,7 +5,7 @@ import { ReStruct } from 'application/render'; import { Scale } from 'domain/helpers'; export class RasterImageMove extends BaseOperation { - constructor(private id: number, private difference: Vec2) { + constructor(private id: number, private offset: Vec2) { super(OperationType.RASTER_IMAGE_MOVE); } @@ -16,16 +16,16 @@ export class RasterImageMove extends BaseOperation { if (!item || !renderItem) { return; } - const differenceScaled = Scale.modelToCanvas( - this.difference, + const scaledOffset = Scale.modelToCanvas( + this.offset, reStruct.render.options, ); - renderItem.move(differenceScaled); - item.addPositionOffset(this.difference); + renderItem.move(scaledOffset); + item.addPositionOffset(this.offset); } invert(): BaseOperation { - return new RasterImageMove(this.id, this.difference.negated()); + return new RasterImageMove(this.id, this.offset.negated()); } } diff --git a/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts index c09ae15f2b..87b074fef8 100644 --- a/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts +++ b/packages/ketcher-core/src/application/editor/operations/rasterImage/rasterImageUpsertDelete.ts @@ -32,7 +32,7 @@ export class RasterImageUpsert extends BaseOperation { if (!this.id) { this.id = struct.rasterImages.newId(); } - const id = this.id!; + const id = this.id; const item = this.rasterImage.clone(); struct.rasterImages.set(id, item); reStruct.rasterImages.set(id, new ReRasterImage(item)); diff --git a/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts b/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts index 90a27c8ae2..d113555445 100644 --- a/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts +++ b/packages/ketcher-core/src/application/render/restruct/rerasterImage.ts @@ -18,7 +18,7 @@ export class ReRasterImage extends ReObject { show(restruct: ReStruct, options: RenderOptions) { if (this.element) { - return; + this.remove(); } const render = restruct.render; diff --git a/packages/ketcher-core/src/domain/entities/rasterImage.ts b/packages/ketcher-core/src/domain/entities/rasterImage.ts index ad39742ca9..db017ebad3 100644 --- a/packages/ketcher-core/src/domain/entities/rasterImage.ts +++ b/packages/ketcher-core/src/domain/entities/rasterImage.ts @@ -19,20 +19,25 @@ import { Vec2 } from 'domain/entities/vec2'; type Position = [Vec2, Vec2]; +export const RASTER_IMAGE_KEY = 'rasterImage'; + export class RasterImage extends BaseMicromoleculeEntity { constructor(public bitmap: string, public position: [Vec2, Vec2]) { super(); } clone(): RasterImage { - return new RasterImage(this.bitmap, this.position); + return new RasterImage( + this.bitmap, + this.position.map((item) => item) as Position, + ); } addPositionOffset(offset: Vec2) { this.position = this.position.map((item) => item.add(offset)) as Position; } - scaled(scale: number): void { + rescalePosition(scale: number): void { this.position = this.position.map((item) => item.scaled(scale)) as Position; } } diff --git a/packages/ketcher-core/src/domain/entities/struct.ts b/packages/ketcher-core/src/domain/entities/struct.ts index a8b2e3e494..66d936bc47 100644 --- a/packages/ketcher-core/src/domain/entities/struct.ts +++ b/packages/ketcher-core/src/domain/entities/struct.ts @@ -837,7 +837,9 @@ export class Struct { simpleObjects.pos = simpleObjects.pos.map((p) => p.scaled(scale)); }); - this.rasterImages.forEach((rasterImage) => rasterImage.scaled(scale)); + this.rasterImages.forEach((rasterImage) => + rasterImage.rescalePosition(scale), + ); } rescale() { diff --git a/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts b/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts index 03bfa08a96..3f70d5ad24 100644 --- a/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts +++ b/packages/ketcher-core/src/domain/serializers/ket/ketSerializer.ts @@ -72,6 +72,7 @@ import { MonomerItemType } from 'domain/types'; import { PolymerBond } from 'domain/entities/PolymerBond'; import { rasterImageToKet } from 'domain/serializers/ket/toKet/rasterImageToKet'; import { rasterImageToStruct } from 'domain/serializers/ket/fromKet/rasterImageToStruct'; +import { RASTER_IMAGE_KEY } from 'domain/entities/rasterImage'; function parseNode(node: any, struct: any) { const type = node.type; @@ -106,7 +107,7 @@ function parseNode(node: any, struct: any) { textToStruct(node, struct); break; } - case 'rasterImage': { + case [RASTER_IMAGE_KEY]: { rasterImageToStruct(node, struct); break; } @@ -180,7 +181,7 @@ export class KetSerializer implements Serializer { result.root.nodes.push(textToKet(item)); break; } - case 'rasterImage': { + case RASTER_IMAGE_KEY: { result.root.nodes.push(rasterImageToKet(item)); break; } diff --git a/packages/ketcher-core/src/domain/serializers/ket/schema.json b/packages/ketcher-core/src/domain/serializers/ket/schema.json index 146d0f7e18..8c34cde26a 100644 --- a/packages/ketcher-core/src/domain/serializers/ket/schema.json +++ b/packages/ketcher-core/src/domain/serializers/ket/schema.json @@ -805,9 +805,6 @@ }, "y": { "type": "number" - }, - "z": { - "type": "number" } } } diff --git a/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts b/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts index 0d4c0b8113..027d3d7dcc 100644 --- a/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts +++ b/packages/ketcher-core/src/domain/serializers/ket/toKet/prepare.ts @@ -14,6 +14,7 @@ * limitations under the License. ***************************************************************************/ import { Pile, Pool, SGroup, Struct, Vec2 } from 'domain/entities'; +import { RASTER_IMAGE_KEY } from 'domain/entities/rasterImage'; type KetNode = { type: string; @@ -99,7 +100,7 @@ export function prepareStructForKet(struct: Struct) { struct.rasterImages.forEach((image) => { const center: Vec2 = Vec2.centre(image.position[0], image.position[1]); ketNodes.push({ - type: 'rasterImage', + type: RASTER_IMAGE_KEY, center, data: { bitmap: image.bitmap, diff --git a/packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts b/packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts index cc2c53d1fd..08f244245d 100644 --- a/packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts +++ b/packages/ketcher-core/src/domain/serializers/ket/toKet/rasterImageToKet.ts @@ -15,10 +15,11 @@ ***************************************************************************/ import { getNodeWithInvertedYCoord } from '../helpers'; +import { RASTER_IMAGE_KEY } from 'domain/entities/rasterImage'; export function rasterImageToKet(rasterImageNode) { return { - type: 'rasterImage', + type: RASTER_IMAGE_KEY, data: getNodeWithInvertedYCoord(rasterImageNode.data), selected: rasterImageNode.selected, };