]> gerrit.midnightthoughts Code Review - neoboard-miro-converter.git/commitdiff
Improve the coordinate system transformation to actually reflect the correct coordinates 61/161/1
authorMTRNord <mtrnord1@gmail.com>
Sat, 11 Jan 2025 23:12:25 +0000 (00:12 +0100)
committerMTRNord <mtrnord1@gmail.com>
Sat, 11 Jan 2025 23:12:25 +0000 (00:12 +0100)
Change-Id: I31629b109236e6f22500332cef99ff32b86663c3

src/app/importActions.ts

index 6033f045377f937c7aec038984a0c44aec7c81a6..0fbcbfc0f854deff837fc6a8e30af4f930c7f050 100644 (file)
@@ -149,29 +149,37 @@ export async function importBoard(prevState: FormState, formData: FormData): Pro
                 const startItem = frameData.children.find(c => c.id === connector.startItem?.id);
                 const endItem = frameData.children.find(c => c.id === connector.endItem?.id);
                 if (startItem && endItem) {
+                    const startItemX = startItem.position?.x ?? 0;
+                    const startItemY = startItem.position?.y ?? 0;
+                    const endItemX = endItem.position?.x ?? 0;
+                    const endItemY = endItem.position?.y ?? 0;
+
+                    const points = [
+                        {
+                            x: startItemX,
+                            y: startItemY,
+                        },
+                        {
+                            x: endItemX,
+                            y: endItemY,
+                        }
+                    ];
+
+                    console.log('points', points);
+
+
                     const path: Path = {
                         type: 'path',
                         kind: 'line',
                         position: {
-                            x: startItem.position?.x ?? 0,
-                            y: startItem.position?.y ?? 0,
+                            x: 0,
+                            y: 0,
                         },
-                        // Relative to the position given above
-                        points: [
-                            // Start point
-                            {
-                                x: 0,
-                                y: 0,
-                            },
-                            // End point
-                            {
-                                x: endItem.position?.x ? endItem.position.x - (startItem.position?.x ?? 0) : 0,
-                                y: endItem.position?.y ? endItem.position.y - (startItem.position?.y ?? 0) : 0,
-                            },
-                        ],
+                        points: points,
                         strokeColor: connector.style?.color ?? '#1a1a1a',
                         endMarker: connector.style?.endStrokeCap === 'arrow' ? 'arrow-head-line' : undefined
                     };
+
                     slide.elements.push(path);
                 }
             }
@@ -197,10 +205,16 @@ export async function importBoard(prevState: FormState, formData: FormData): Pro
  * The algorithm should:
  * 1. Find the bounding box of all the items in the slide
  * 2. Scale all the items to fit within the bounding box which is the maximum size of the frame minus some padding
- * 3. Center the items in the frame
- * 4. Return the slides with the transformed items 
+ * 3. Align the items top left in the neoboard frame
+ * 4. Return the slides with the transformed items
+ * 
+ * Note that the points of a path are relative to the path position and not the frame position.
+ * Note that that all the coordinates coming in are in the miro coordinate system which is likely larger than the neoboard frame.
+ * Note that the miro coordinate system is top left based and the neoboard frame is too.
+ * Note that the coordinates in miro are the center of the item and not a corner.
  * 
  * @param board The board with items which should be fitted into the frame's maximum size
+ * @returns The board with the items transformed to fit into the frame
  */
 function fitItemsBestIntoFrame(board: Whiteboard): Whiteboard {
     const neoboardPadding = 10;
@@ -212,85 +226,75 @@ function fitItemsBestIntoFrame(board: Whiteboard): Whiteboard {
         }
     };
 
-    // Note that all the coordinates are relative to the top left corner of the frame and still in the miro coordinate system which is likely larger than the neoboard frame.
-
     for (const slide of board.whiteboard.slides) {
         const transformedSlide: Slide = {
             elements: [],
         };
 
-        // The first step is to find the bounding box of all the slides items.
-        let minX = Number.MAX_SAFE_INTEGER;
-        let minY = Number.MAX_SAFE_INTEGER;
-        let maxX = Number.MIN_SAFE_INTEGER;
-        let maxY = Number.MIN_SAFE_INTEGER;
-
-        for (const element of slide.elements) {
-            if (element.type === 'shape' || element.type === 'image') {
-                const x = element.position.x;
-                const y = element.position.y;
-                const width = element.width;
-                const height = element.height;
-
-                minX = Math.min(minX, x);
-                minY = Math.min(minY, y);
-                maxX = Math.max(maxX, x + width);
-                maxY = Math.max(maxY, y + height);
-            } else if (element.type === 'path') {
-                // The points are relative to the position of the path
-                const x = element.position.x;
-                const y = element.position.y;
-                const points = element.points;
-
-                minX = Math.min(minX, x);
-                minY = Math.min(minY, y);
-                maxX = Math.max(maxX, x + Math.max(...points.map(p => p.x)));
-                maxY = Math.max(maxY, y + Math.max(...points.map(p => p.y)));
+        // Find the bounding box of all the items in the slide
+        let minX = Number.MAX_VALUE;
+        let minY = Number.MAX_VALUE;
+        let maxX = Number.MIN_VALUE;
+        let maxY = Number.MIN_VALUE;
+
+        for (const item of slide.elements) {
+            if (item.type === 'shape' || item.type === 'image') {
+                const shape = item as Shape;
+                // Make sure that we deal with x and y being in the center of the shape and not the corner.
+                // This means the bounds of the shape itself are x - width / 2, y - height / 2, x + width / 2, y + height / 2
+
+
+                minX = Math.min(minX, shape.position.x - shape.width / 2);
+                minY = Math.min(minY, shape.position.y - shape.height / 2);
+                maxX = Math.max(maxX, shape.position.x + shape.width / 2);
+                maxY = Math.max(maxY, shape.position.y + shape.height / 2);
+            } else if (item.type === 'path') {
+                // We know that our paths are always relative to 0,0 as we set the position to 0,0
+                const path = item as Path;
+                for (const point of path.points) {
+                    minX = Math.min(minX, point.x);
+                    minY = Math.min(minY, point.y);
+                    maxX = Math.max(maxX, point.x);
+                    maxY = Math.max(maxY, point.y);
+                }
             }
         }
 
-        // The second step is to scale all the elements to fill the frame which is (neoboardWhiteboardWidth - neoboardPadding) wide and (neoboardWhiteboardHeight - neoboardPadding) tall.
-        // We treat the size we want to fit the grouped size of all the items in the slide.
-        // We also want this group to be centered in the frame.
-        const width = maxX - minX;
-        const height = maxY - minY;
-        const scale = Math.min((neoboardWhiteboardWidth - neoboardPadding) / width, (neoboardWhiteboardHeight - neoboardPadding) / height);
-
-        for (const element of slide.elements) {
-            if (element.type === 'shape' || element.type === 'image') {
-                const x = (element.position.x - minX) * scale + neoboardPadding / 2;
-                const y = (element.position.y - minY) * scale + neoboardPadding / 2;
-                const width = element.width * scale;
-                const height = element.height * scale;
-
-                transformedSlide.elements.push({
-                    ...element,
-                    position: {
-                        x,
-                        y,
-                    },
-                    width,
-                    height,
-                });
-            } else if (element.type === 'path') {
-                // The points are relative to the position of the path
-                const x = (element.position.x - minX) * scale + neoboardPadding / 2;
-                const y = (element.position.y - minY) * scale + neoboardPadding / 2;
-                const points = element.points.map(p => ({
-                    x: p.x * scale,
-                    y: p.y * scale,
-                }));
-
-                transformedSlide.elements.push({
-                    ...element,
+        // Scale all the items to fit within the bounding box which is the maximum size of the frame minus some padding
+        const boundingBoxWidth = maxX - minX;
+        const boundingBoxHeight = maxY - minY;
+
+        const scale = Math.min((neoboardWhiteboardWidth - neoboardPadding) / boundingBoxWidth, (neoboardWhiteboardHeight - neoboardPadding) / boundingBoxHeight);
+
+        for (const item of slide.elements) {
+            if (item.type === 'shape' || item.type === 'image') {
+                const shape = item;
+                // Ensure we account for the fact that our coordinates are in the center of the shape currently but we require them to be at the top left
+                // This means we need to substract half of the width from the x and substract half of the height to the y
+                const transformedShape = {
+                    ...shape,
                     position: {
-                        x,
-                        y,
+                        x: (shape.position.x - minX - shape.width / 2) * scale + neoboardPadding,
+                        y: (shape.position.y - minY - shape.height / 2) * scale + neoboardPadding,
                     },
-                    points,
-                });
-            } else {
-                transformedSlide.elements.push(element);
+                    width: shape.width * scale,
+                    height: shape.height * scale,
+                };
+
+
+                transformedSlide.elements.push(transformedShape);
+            } else if (item.type === 'path') {
+                // We want to keep the path set as 0,0 and only scale the points as they are relative to the path position
+                const path = item as Path;
+                const transformedPath = {
+                    ...path,
+                    points: path.points.map(point => ({
+                        x: (point.x - minX) * scale + neoboardPadding,
+                        y: (point.y - minY) * scale + neoboardPadding,
+                    }))
+                };
+
+                transformedSlide.elements.push(transformedPath);
             }
         }