From 28ebdc30a7a81989443b18f43e6fefce8ed0a4f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mi=C5=82osz=20Grocholewski?= <m.grocholewski@atcomp.pl>
Date: Thu, 21 Nov 2024 09:52:46 +0100
Subject: [PATCH] Resolve MIN-77 "Feat/ rendering optimization"

---
 .../MapViewerVector.constants.ts              | 26 ++++++++
 .../reactionsLayer/processModelElements.ts    |  1 +
 .../utils/shapes/elements/BaseMultiPolygon.ts | 65 +++++++++++++------
 .../utils/shapes/elements/Compartment.ts      |  8 ++-
 .../shapes/elements/CompartmentPathway.ts     |  5 +-
 .../utils/shapes/elements/MapElement.test.ts  |  1 +
 .../utils/shapes/elements/MapElement.ts       | 21 ++++--
 .../utils/shapes/elements/getShapePolygon.ts  |  1 +
 .../utils/shapes/layer/Layer.ts               | 26 ++++++--
 .../utils/shapes/reaction/Reaction.ts         | 31 +++++++--
 10 files changed, 149 insertions(+), 36 deletions(-)

diff --git a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
index 9a036ac1..0cd65f9a 100644
--- a/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants.ts
@@ -2,6 +2,8 @@ import { Color, ShapeRelAbs, ShapeRelAbsBezierPoint } from '@/types/models';
 
 export const VECTOR_MAP_LAYER_TYPE = 'vectorMapLayer';
 
+export const COMPLEX_SBO_TERM = 'SBO:0000253';
+
 export const WHITE_COLOR: Color = {
   alpha: 255,
   rgb: 16777215,
@@ -12,6 +14,30 @@ export const BLACK_COLOR: Color = {
   rgb: -16777216,
 };
 
+export const REACTION_ELEMENT_TYPES = {
+  OPERATOR: 'operator',
+  SQUARE: 'square',
+  LINE: 'line',
+  ARROW: 'arrow',
+};
+
+export const MAP_ELEMENT_TYPES = {
+  TEXT: 'text',
+  MODIFICATION: 'modification',
+  ACTIVITY_BORDER: 'activityBorder',
+  ENTITY: 'entity',
+  OVERLAY: 'overlay',
+  COMPARTMENT: 'compartment',
+};
+
+export const LAYER_ELEMENT_TYPES = {
+  TEXT: 'text',
+  OVAL: 'oval',
+  RECT: 'rect',
+  LINE: 'line',
+  ARROW: 'arrow',
+};
+
 export const COMPARTMENT_SQUARE_POINTS: Array<ShapeRelAbs | ShapeRelAbsBezierPoint> = [
   {
     type: 'REL_ABS_POINT',
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts
index 8aba562e..066712d0 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/processModelElements.ts
@@ -83,6 +83,7 @@ export default function processModelElements(
       validElements.push(
         new MapElement({
           id: element.id,
+          sboTerm: element.sboTerm,
           shapes: elementShapes,
           x: element.x,
           y: element.y,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
index 84dfd159..b53e6ee6 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon.ts
@@ -13,9 +13,14 @@ import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shape
 import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/rgbToHex';
 import getTextCoords from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextCoords';
 import { Color } from '@/types/models';
+import {
+  COMPLEX_SBO_TERM,
+  MAP_ELEMENT_TYPES,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 
 export interface BaseMapElementProps {
   type: string;
+  sboTerm?: string;
   id: number;
   x: number;
   y: number;
@@ -39,6 +44,8 @@ export interface BaseMapElementProps {
 export default abstract class BaseMultiPolygon {
   type: string;
 
+  sboTerm: string | undefined;
+
   id: number;
 
   x: number;
@@ -77,16 +84,13 @@ export default abstract class BaseMultiPolygon {
 
   styles: Array<Style> = [];
 
-  polygonsTexts: Array<string> = [];
-
-  lineWidths: Array<number> = [];
-
   feature: Feature = new Feature();
 
   pointToProjection: UsePointToProjectionResult;
 
   constructor({
     type,
+    sboTerm,
     id,
     x,
     y,
@@ -107,6 +111,7 @@ export default abstract class BaseMultiPolygon {
     pointToProjection,
   }: BaseMapElementProps) {
     this.type = type;
+    this.sboTerm = sboTerm;
     this.id = id;
     this.x = x;
     this.y = y;
@@ -142,6 +147,9 @@ export default abstract class BaseMultiPolygon {
         pointToProjection: this.pointToProjection,
       });
       const textPolygon = new Polygon([[textCoords, textCoords]]);
+      textPolygon.set('type', MAP_ELEMENT_TYPES.TEXT);
+      textPolygon.set('text', this.text);
+      textPolygon.set('fontSize', this.fontSize);
       const textStyle = getTextStyle({
         text: this.text,
         fontSize: this.fontSize,
@@ -151,7 +159,6 @@ export default abstract class BaseMultiPolygon {
       });
       textStyle.setGeometry(textPolygon);
       this.styles.push(textStyle);
-      this.polygonsTexts.push(this.text);
       this.polygons.push(textPolygon);
     }
   }
@@ -177,29 +184,49 @@ export default abstract class BaseMultiPolygon {
   }
 
   protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
+    const styles: Array<Style> = [];
     const getTextScale = feature.get('getTextScale');
     let textScale = 1;
     if (getTextScale instanceof Function) {
       textScale = getTextScale(resolution);
     }
-    let textIndex = 0;
-    let strokeIndex = 0;
+
+    let type: string;
+    let fontSize: number;
+    let lineWidth: number;
+    let text: string;
+
     this.styles.forEach(style => {
-      if (style.getText()) {
-        if (this.fontSize * textScale > 4) {
-          style.getText()?.setScale(textScale);
-          style.getText()?.setText(this.polygonsTexts[textIndex]);
-          textIndex += 1;
+      const styleGeometry = style.getGeometry();
+      if (styleGeometry instanceof Polygon) {
+        type = styleGeometry.get('type');
+        text = styleGeometry.get('text');
+        fontSize = styleGeometry.get('fontSize');
+        lineWidth = styleGeometry.get('lineWidth');
+      }
+      if (
+        [MAP_ELEMENT_TYPES.MODIFICATION, MAP_ELEMENT_TYPES.TEXT].includes(type) &&
+        textScale * fontSize <= 4
+      ) {
+        return;
+      }
+
+      const clonedStyle = style.clone();
+      const textStyle = clonedStyle.getText();
+      const strokeStyle = clonedStyle.getStroke();
+      if (type === 'text' && textStyle) {
+        textStyle.setScale(textScale);
+        textStyle.setText(text);
+      }
+      if (strokeStyle && lineWidth) {
+        if (lineWidth * textScale < 0.08 && this.sboTerm !== COMPLEX_SBO_TERM) {
+          clonedStyle.setStroke(null);
         } else {
-          style.getText()?.setText(undefined);
+          strokeStyle.setWidth(lineWidth * textScale);
         }
       }
-      if (style.getStroke()) {
-        const lineWidth = this.lineWidths[strokeIndex] * textScale;
-        style.getStroke()?.setWidth(lineWidth);
-        strokeIndex += 1;
-      }
+      styles.push(clonedStyle);
     });
-    return this.styles;
+    return styles;
   }
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts
index b1aca3d8..9c50b1bc 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/Compartment.ts
@@ -13,6 +13,7 @@ import { rgbToHex } from '@/components/Map/MapViewer/MapViewerVector/utils/shape
 import getStroke from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStroke';
 import { MapInstance } from '@/types/map';
 import { Color } from '@/types/models';
+import { MAP_ELEMENT_TYPES } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 
 export interface CompartmentProps {
   id: number;
@@ -108,6 +109,7 @@ export default abstract class Compartment extends BaseMultiPolygon {
 
   protected createPolygons(): void {
     const framePolygon = new Polygon([this.outerCoords, this.innerCoords]);
+    framePolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT);
     this.styles.push(
       new Style({
         geometry: framePolygon,
@@ -118,6 +120,8 @@ export default abstract class Compartment extends BaseMultiPolygon {
     this.polygons.push(framePolygon);
 
     const outerPolygon = new Polygon([this.outerCoords]);
+    outerPolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT);
+    outerPolygon.set('lineWidth', this.outerWidth);
     this.styles.push(
       new Style({
         geometry: outerPolygon,
@@ -125,10 +129,11 @@ export default abstract class Compartment extends BaseMultiPolygon {
         zIndex: this.zIndex,
       }),
     );
-    this.lineWidths.push(this.outerWidth);
     this.polygons.push(outerPolygon);
 
     const innerPolygon = new Polygon([this.innerCoords]);
+    innerPolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT);
+    innerPolygon.set('lineWidth', this.innerWidth);
     this.styles.push(
       new Style({
         geometry: innerPolygon,
@@ -137,7 +142,6 @@ export default abstract class Compartment extends BaseMultiPolygon {
         zIndex: this.zIndex,
       }),
     );
-    this.lineWidths.push(this.innerWidth);
     this.polygons.push(innerPolygon);
   }
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts
index 8adec3ec..bcc70aa1 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/CompartmentPathway.ts
@@ -7,6 +7,7 @@ import {
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
 import {
   BLACK_COLOR,
+  MAP_ELEMENT_TYPES,
   WHITE_COLOR,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import Polygon from 'ol/geom/Polygon';
@@ -98,7 +99,9 @@ export default class CompartmentPathway extends BaseMultiPolygon {
         this.pointToProjection({ x: this.x, y: this.y + this.height }),
       ],
     ]);
-    this.lineWidths.push(this.outerWidth);
+    compartmentPolygon.set('type', MAP_ELEMENT_TYPES.COMPARTMENT);
+    compartmentPolygon.set('lineWidth', this.outerWidth);
+
     this.styles.push(
       getStyle({
         geometry: compartmentPolygon,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
index 71c5c3c2..81665cc6 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.test.ts
@@ -40,6 +40,7 @@ describe('MapElement', () => {
     });
     props = {
       id: 1,
+      sboTerm: 'SBO:2313123',
       shapes: shapesFixture,
       x: 0,
       y: 0,
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
index f485c3fb..575d8d77 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/MapElement.ts
@@ -13,6 +13,7 @@ import {
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.types';
 import {
   BLACK_COLOR,
+  MAP_ELEMENT_TYPES,
   WHITE_COLOR,
 } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import BaseMultiPolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/BaseMultiPolygon';
@@ -30,6 +31,7 @@ import { GetOverlayBioEntityColorByAvailableProperties } from '@/components/Map/
 
 export type MapElementProps = {
   id: number;
+  sboTerm: string;
   shapes: Array<Shape>;
   x: number;
   y: number;
@@ -88,6 +90,7 @@ export default class MapElement extends BaseMultiPolygon {
 
   constructor({
     id,
+    sboTerm,
     shapes,
     x,
     y,
@@ -120,6 +123,7 @@ export default class MapElement extends BaseMultiPolygon {
   }: MapElementProps) {
     super({
       type: FEATURE_TYPE.ALIAS,
+      sboTerm,
       id,
       x,
       y,
@@ -194,13 +198,15 @@ export default class MapElement extends BaseMultiPolygon {
         pointToProjection: this.pointToProjection,
         mirror: modification.direction && modification.direction === 'RIGHT',
       });
+      modificationPolygon.set('type', MAP_ELEMENT_TYPES.MODIFICATION);
+      modificationPolygon.set('fontSize', modification.fontSize);
+      modificationPolygon.set('lineWidth', 1);
       const modificationStyle = new Style({
         geometry: modificationPolygon,
         stroke: getStroke({ color: rgbToHex(modification.borderColor) }),
         fill: getFill({ color: rgbToHex(modification.fillColor) }),
         zIndex: modification.z,
       });
-      this.lineWidths.push(1);
       this.polygons.push(modificationPolygon);
       this.styles.push(modificationStyle);
     });
@@ -222,6 +228,9 @@ export default class MapElement extends BaseMultiPolygon {
       const modificationTextPolygon = new Polygon([
         [modificationTextCoords, modificationTextCoords],
       ]);
+      modificationTextPolygon.set('type', MAP_ELEMENT_TYPES.TEXT);
+      modificationTextPolygon.set('text', modificationText);
+      modificationTextPolygon.set('fontSize', modification.fontSize);
       const modificationTextStyle = getTextStyle({
         text: modificationText,
         fontSize: modification.fontSize,
@@ -231,7 +240,6 @@ export default class MapElement extends BaseMultiPolygon {
       });
       modificationTextStyle.setGeometry(modificationTextPolygon);
       this.styles.push(modificationTextStyle);
-      this.polygonsTexts.push(modificationText);
       this.polygons.push(modificationTextPolygon);
     }
   }
@@ -246,6 +254,8 @@ export default class MapElement extends BaseMultiPolygon {
         height: this.height - homodimerOffset + 10,
         pointToProjection: this.pointToProjection,
       });
+      activityBorderPolygon.set('type', MAP_ELEMENT_TYPES.ACTIVITY_BORDER);
+      activityBorderPolygon.set('lineWidth', 1);
       const activityBorderStyle = getStyle({
         geometry: activityBorderPolygon,
         fillColor: { rgb: 0, alpha: 0 },
@@ -253,7 +263,6 @@ export default class MapElement extends BaseMultiPolygon {
         zIndex: this.zIndex,
       });
       this.polygons.push(activityBorderPolygon);
-      this.lineWidths.push(1);
       this.styles.push(activityBorderStyle);
     });
   }
@@ -268,6 +277,8 @@ export default class MapElement extends BaseMultiPolygon {
         height: this.height - homodimerOffset,
         pointToProjection: this.pointToProjection,
       });
+      elementPolygon.set('type', MAP_ELEMENT_TYPES.ENTITY);
+      elementPolygon.set('lineWidth', this.lineWidth);
       const elementStyle = getStyle({
         geometry: elementPolygon,
         borderColor: this.borderColor,
@@ -277,7 +288,6 @@ export default class MapElement extends BaseMultiPolygon {
         zIndex: this.zIndex,
       });
       this.polygons.push(elementPolygon);
-      this.lineWidths.push(this.lineWidth);
       this.styles.push(elementStyle);
     });
   }
@@ -303,6 +313,8 @@ export default class MapElement extends BaseMultiPolygon {
           this.pointToProjection({ x: xMin, y: entity.y2 }),
         ],
       ]);
+      polygon.set('type', MAP_ELEMENT_TYPES.OVERLAY);
+      polygon.set('lineWidth', 1);
       const style = getStyle({
         geometry: polygon,
         borderColor: color,
@@ -310,7 +322,6 @@ export default class MapElement extends BaseMultiPolygon {
         zIndex: this.zIndex,
       });
       this.polygons.push(polygon);
-      this.lineWidths.push(1);
       this.styles.push(style);
     });
   }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.ts
index ef748554..7e2f7a59 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon.ts
@@ -48,6 +48,7 @@ export default function getShapePolygon({
       return [mirroredX, coord[1]];
     });
   }
+  coords.push(coords[0]);
 
   return new Polygon([coords]);
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
index 0554c9db..dd8b81dc 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/layer/Layer.ts
@@ -19,6 +19,7 @@ import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/sh
 import { FeatureLike } from 'ol/Feature';
 import Style from 'ol/style/Style';
 import { ArrowTypeDict, LineTypeDict } from '@/redux/shapes/shapes.types';
+import { LAYER_ELEMENT_TYPES } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 
 export interface LayerProps {
   texts: Array<LayerText>;
@@ -151,6 +152,7 @@ export default class Layer {
         geometry: polygon,
         style: polygonStyle,
         lineWidth: rect.lineWidth,
+        elementType: LAYER_ELEMENT_TYPES.RECT,
       });
       rectFeature.setStyle(this.getStyle.bind(this));
       return rectFeature;
@@ -179,6 +181,7 @@ export default class Layer {
         geometry: polygon,
         style: polygonStyle,
         lineWidth: oval.lineWidth,
+        elementType: LAYER_ELEMENT_TYPES.OVAL,
       });
       ovalFeature.setStyle(this.getStyle.bind(this));
       return ovalFeature;
@@ -227,6 +230,7 @@ export default class Layer {
           pointToProjection: this.pointToProjection,
         });
         if (startArrowFeature) {
+          startArrowFeature.set('elementType', LAYER_ELEMENT_TYPES.ARROW);
           startArrowFeature.set('lineWidth', line.width);
           startArrowFeature.setStyle(this.getStyle.bind(this));
           arrowsFeatures.push(startArrowFeature);
@@ -255,6 +259,7 @@ export default class Layer {
           pointToProjection: this.pointToProjection,
         });
         if (endArrowFeature) {
+          endArrowFeature.set('elementType', LAYER_ELEMENT_TYPES.ARROW);
           endArrowFeature.set('lineWidth', line.width);
           endArrowFeature.setStyle(this.getStyle.bind(this));
           arrowsFeatures.push(endArrowFeature);
@@ -275,6 +280,7 @@ export default class Layer {
         geometry: lineString,
         style: lineStyle,
         lineWidth: line.width,
+        elementType: LAYER_ELEMENT_TYPES.LINE,
       });
       lineFeature.setStyle(this.getStyle.bind(this));
       linesFeatures.push(lineFeature);
@@ -283,6 +289,7 @@ export default class Layer {
   };
 
   protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
+    const styles: Array<Style> = [];
     const maxZoom = this.mapInstance?.getView().get('originalMaxZoom');
     const minResolution = this.mapInstance?.getView().getResolutionForZoom(maxZoom);
     const style = feature.get('style');
@@ -292,16 +299,25 @@ export default class Layer {
 
     const scale = minResolution / resolution;
     const lineWidth = feature.get('lineWidth') * scale;
+    const type = feature.get('elementType');
 
-    if (style instanceof Style && style.getStroke()) {
-      style.getStroke()?.setWidth(lineWidth);
+    if (type === LAYER_ELEMENT_TYPES.ARROW && scale <= 0.08) {
+      return [];
+    }
+    let clonedStyle: Style;
+    if (style instanceof Style) {
+      clonedStyle = style.clone();
+      clonedStyle.getStroke()?.setWidth(lineWidth);
+      styles.push(clonedStyle);
     } else if (Array.isArray(style)) {
       style.forEach(singleStyle => {
-        if (singleStyle instanceof Style && singleStyle.getStroke()) {
-          singleStyle.getStroke()?.setWidth(lineWidth);
+        if (singleStyle instanceof Style) {
+          clonedStyle = singleStyle.clone();
+          clonedStyle.getStroke()?.setWidth(lineWidth);
+          styles.push(clonedStyle);
         }
       });
     }
-    return style;
+    return styles;
   }
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
index 9bd713ce..c4fc7499 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
@@ -9,7 +9,10 @@ import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/sh
 import getShapePolygon from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getShapePolygon';
 import Polygon from 'ol/geom/Polygon';
 import Style from 'ol/style/Style';
-import { WHITE_COLOR } from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
+import {
+  REACTION_ELEMENT_TYPES,
+  WHITE_COLOR,
+} from '@/components/Map/MapViewer/MapViewerVector/MapViewerVector.constants';
 import { FeatureLike } from 'ol/Feature';
 import { MapInstance } from '@/types/map';
 import getTextStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/text/getTextStyle';
@@ -157,6 +160,7 @@ export default class Reaction {
         pointToProjection: this.pointToProjection,
       });
       if (startArrowFeature) {
+        startArrowFeature.set('elementType', REACTION_ELEMENT_TYPES.ARROW);
         startArrowFeature.set('lineWidth', line.width);
         startArrowFeature.setStyle(this.getStyle.bind(this));
         arrowsFeatures.push(startArrowFeature);
@@ -184,6 +188,7 @@ export default class Reaction {
         pointToProjection: this.pointToProjection,
       });
       if (endArrowFeature) {
+        endArrowFeature.set('elementType', REACTION_ELEMENT_TYPES.ARROW);
         endArrowFeature.set('lineWidth', line.width);
         endArrowFeature.setStyle(this.getStyle.bind(this));
         arrowsFeatures.push(endArrowFeature);
@@ -206,6 +211,7 @@ export default class Reaction {
       lineWidth: line.width,
       id: this.id,
       type: FEATURE_TYPE.REACTION,
+      elementType: REACTION_ELEMENT_TYPES.LINE,
     });
     lineFeature.setStyle(this.getStyle.bind(this));
 
@@ -254,6 +260,7 @@ export default class Reaction {
       lineWidth: this.line.width,
       id: this.id,
       type: FEATURE_TYPE.REACTION,
+      elementType: REACTION_ELEMENT_TYPES.SQUARE,
     });
     squareFeature.setStyle(this.getStyle.bind(this));
     return squareFeature;
@@ -308,12 +315,15 @@ export default class Reaction {
       lineWidth: 1,
       id: this.id,
       type: FEATURE_TYPE.REACTION,
+      elementType: REACTION_ELEMENT_TYPES.OPERATOR,
+      fontSize: 10,
     });
     circleFeature.setStyle(this.getStyle.bind(this));
     return circleFeature;
   }
 
   protected getStyle(feature: FeatureLike, resolution: number): Style | Array<Style> | void {
+    const styles: Array<Style> = [];
     const maxZoom = this.mapInstance?.getView().get('originalMaxZoom');
     const minResolution = this.mapInstance?.getView().getResolutionForZoom(maxZoom);
     const style = feature.get('style');
@@ -323,16 +333,29 @@ export default class Reaction {
 
     const scale = minResolution / resolution;
     const lineWidth = feature.get('lineWidth') * scale;
+    const type = feature.get('elementType');
+    const fontSize = feature.get('fontSize');
+
+    if (type === REACTION_ELEMENT_TYPES.OPERATOR && fontSize * scale <= 4) {
+      return [];
+    }
+    if (type === REACTION_ELEMENT_TYPES.ARROW && scale <= 0.08) {
+      return [];
+    }
 
-    if (style instanceof Style && style.getStroke()) {
+    if (style instanceof Style) {
       style.getStroke()?.setWidth(lineWidth);
+      style.getText()?.setScale(scale);
+      styles.push(style);
     } else if (Array.isArray(style)) {
       style.forEach(singleStyle => {
-        if (singleStyle instanceof Style && singleStyle.getStroke()) {
+        if (singleStyle instanceof Style) {
           singleStyle.getStroke()?.setWidth(lineWidth);
+          singleStyle.getText()?.setScale(scale);
+          styles.push(singleStyle);
         }
       });
     }
-    return style;
+    return styles;
   }
 }
-- 
GitLab