From dfca2762c72b8c83a9d5e0d69d671fe4efc62ccf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mi=C5=82osz=20Grocholewski?= <m.grocholewski@atcomp.pl>
Date: Mon, 4 Nov 2024 10:10:31 +0100
Subject: [PATCH] feat(vector-map): implement reaction square element

---
 .../reactionsLayer/useOlMapReactionsLayer.ts  |  7 +-
 .../utils/shapes/coords/getPolygonCoords.ts   |  4 +-
 .../utils/shapes/reaction/Reaction.test.ts    |  4 +-
 .../utils/shapes/reaction/Reaction.ts         | 67 +++++++++++++++++--
 4 files changed, 73 insertions(+), 9 deletions(-)

diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
index 66ddd448..2b3680b0 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/config/reactionsLayer/useOlMapReactionsLayer.ts
@@ -50,6 +50,10 @@ export const useOlMapReactionsLayer = ({
 
   const reactions = useMemo(() => {
     return modelReactions.map(reaction => {
+      const shape = shapes.find(bioShape => bioShape.sboTerm === reaction.sboTerm);
+      if (!shape) {
+        return [];
+      }
       const reactionObject = new Reaction({
         line: reaction.line,
         products: reaction.products,
@@ -57,11 +61,12 @@ export const useOlMapReactionsLayer = ({
         zIndex: reaction.z,
         lineTypes,
         arrowTypes,
+        shapes: shape.shapes,
         pointToProjection,
       });
       return reactionObject.features;
     });
-  }, [arrowTypes, lineTypes, modelReactions, pointToProjection]);
+  }, [arrowTypes, lineTypes, modelReactions, pointToProjection, shapes]);
 
   const elements: Array<
     MapElement | CompartmentCircle | CompartmentSquare | CompartmentPathway | Glyph
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
index f0fd77ba..69af893a 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getPolygonCoords.ts
@@ -45,6 +45,8 @@ export default function getPolygonCoords({
       return [[...lastPoint]];
     }
     const { p1, p2, p3 } = getCurveCoords({ x, y, point, height, width, pointToProjection });
-    return getBezierCurve({ p0: lastPoint, p1, p2, p3, numPoints: 20 });
+    const p0 = lastPoint;
+    lastPoint = p3;
+    return getBezierCurve({ p0, p1, p2, p3, numPoints: 20 });
   });
 }
diff --git a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
index b3d84f4b..2b3e02ca 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.test.ts
@@ -6,6 +6,7 @@ import Reaction, {
 import { newReactionFixture } from '@/models/fixtures/newReactionFixture';
 import { lineTypesFixture } from '@/models/fixtures/lineTypesFixture';
 import { arrowTypesFixture } from '@/models/fixtures/arrowTypesFixture';
+import { shapesFixture } from '@/models/fixtures/shapesFixture';
 
 describe('Layer', () => {
   let props: ReactionProps;
@@ -15,6 +16,7 @@ describe('Layer', () => {
       line: newReactionFixture.line,
       products: newReactionFixture.products,
       reactants: newReactionFixture.reactants,
+      shapes: shapesFixture,
       zIndex: newReactionFixture.z,
       pointToProjection: jest.fn(() => [10, 10]),
       lineTypes: lineTypesFixture,
@@ -25,7 +27,7 @@ describe('Layer', () => {
   it('should initialize a Reaction class', () => {
     const reaction = new Reaction(props);
 
-    expect(reaction.features.length).toBe(5);
+    expect(reaction.features.length).toBe(6);
     expect(reaction.features).toBeInstanceOf(Array<Feature>);
   });
 });
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 4b084d81..d1ace47e 100644
--- a/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
+++ b/src/components/Map/MapViewer/MapViewerVector/utils/shapes/reaction/Reaction.ts
@@ -1,11 +1,15 @@
 /* eslint-disable no-magic-numbers */
-import { ArrowType, Line, LineType, ReactionProduct } from '@/types/models';
+import { ArrowType, Line, LineType, ReactionProduct, Shape } from '@/types/models';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
 import { Feature } from 'ol';
 import { LineString, MultiPolygon } from 'ol/geom';
 import getRotation from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/coords/getRotation';
 import getStyle from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/style/getStyle';
 import getArrowFeature from '@/components/Map/MapViewer/MapViewerVector/utils/shapes/elements/getArrowFeature';
+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';
 
 export interface ReactionProps {
   line: Line;
@@ -14,6 +18,7 @@ export interface ReactionProps {
   zIndex: number;
   lineTypes: Array<LineType>;
   arrowTypes: Array<ArrowType>;
+  shapes: Array<Shape>;
   pointToProjection: UsePointToProjectionResult;
 }
 
@@ -30,6 +35,8 @@ export default class Reaction {
 
   arrowTypes: Array<ArrowType>;
 
+  shapes: Array<Shape>;
+
   pointToProjection: UsePointToProjectionResult;
 
   features: Array<Feature> = [];
@@ -41,6 +48,7 @@ export default class Reaction {
     zIndex,
     lineTypes,
     arrowTypes,
+    shapes,
     pointToProjection,
   }: ReactionProps) {
     this.line = line;
@@ -49,7 +57,15 @@ export default class Reaction {
     this.zIndex = zIndex;
     this.lineTypes = lineTypes;
     this.arrowTypes = arrowTypes;
+    this.shapes = shapes;
     this.pointToProjection = pointToProjection;
+
+    this.drawReaction();
+  }
+
+  private drawReaction(): void {
+    const reactionSquareFeature = this.getReactionSquare();
+    this.features.push(reactionSquareFeature);
     let lineFeature = this.getLineFeature(this.line);
     this.features.push(lineFeature.lineFeature);
     this.features.push(...lineFeature.arrowsFeatures);
@@ -65,12 +81,10 @@ export default class Reaction {
     });
   }
 
-  private getLineFeature = (
-    line: Line,
-  ): {
+  private getLineFeature(line: Line): {
     lineFeature: Feature<LineString>;
     arrowsFeatures: Array<Feature<MultiPolygon>>;
-  } => {
+  } {
     const arrowsFeatures: Array<Feature<MultiPolygon>> = [];
     const points = line.segments
       .map((segment, index) => {
@@ -155,5 +169,46 @@ export default class Reaction {
     lineFeature.setStyle(lineStyle);
 
     return { lineFeature, arrowsFeatures };
-  };
+  }
+
+  private getReactionSquare(): Feature<MultiPolygon> {
+    const polygons: Array<Polygon> = [];
+    const styles: Array<Style> = [];
+    const firstSegment = this.line.segments[0];
+    const squareRotation = getRotation(
+      [firstSegment.x1, firstSegment.y1],
+      [firstSegment.x2, firstSegment.y2],
+    );
+    const squareX = (firstSegment.x1 + firstSegment.x2) / 2;
+    const squareY = (firstSegment.y1 + firstSegment.y2) / 2;
+    this.shapes.forEach(shape => {
+      const squarePolygon = getShapePolygon({
+        shape,
+        x: squareX - 5,
+        y: squareY - 5,
+        width: 10,
+        height: 10,
+        pointToProjection: this.pointToProjection,
+      });
+      const squareStyle = getStyle({
+        geometry: squarePolygon,
+        fillColor: WHITE_COLOR,
+        zIndex: this.zIndex + 1,
+      });
+      squarePolygon.rotate(
+        squareRotation,
+        this.pointToProjection({
+          x: squareX,
+          y: squareY,
+        }),
+      );
+      polygons.push(squarePolygon);
+      styles.push(squareStyle);
+    });
+    const squareFeature = new Feature({
+      geometry: new MultiPolygon(polygons),
+    });
+    squareFeature.setStyle(styles);
+    return squareFeature;
+  }
 }
-- 
GitLab