]> gerrit.midnightthoughts Code Review - neoboard-miro-converter.git/commitdiff
Improve the transformation from miro to neoboard 44/144/2
authorMTRNord <mtrnord1@gmail.com>
Sat, 11 Jan 2025 20:14:14 +0000 (21:14 +0100)
committerMTRNord <mtrnord1@gmail.com>
Sat, 11 Jan 2025 20:51:47 +0000 (21:51 +0100)
Change-Id: Ia3ff34d6a5fc474d94e737c9bd08b08eea916aa2

src/app/importActions.ts

index 27d9bec75a1467c614f29684a4397249dd3d9418..6033f045377f937c7aec038984a0c44aec7c81a6 100644 (file)
@@ -180,9 +180,9 @@ export async function importBoard(prevState: FormState, formData: FormData): Pro
         }
 
         // Transform the board into neoboard coordinates
-        const transformedNeoboard = transformBoardIntoNeoboardCoordinates(neoboard);
+        const fittedNeoboard = fitItemsBestIntoFrame(neoboard);
 
-        resultingBoards.push({ id: boardId, neoboard: transformedNeoboard });
+        resultingBoards.push({ id: boardId, neoboard: fittedNeoboard });
     }
 
     return {
@@ -191,19 +191,40 @@ export async function importBoard(prevState: FormState, formData: FormData): Pro
     }
 }
 
-function transformBoardIntoNeoboardCoordinates(board: Whiteboard): Whiteboard {
-    // The coordinate system in Miro is different from the coordinate system in Neoboard.
-    // In Neoboard the frames are only neoboardWhiteboardWidth wide and whiteboardHeight tall.
-
+/**
+ * The transform doesn't perfectly use the available space in the frame. We need to fit the items into the frame's maximum size.
+ * 
+ * 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 
+ * 
+ * @param board The board with items which should be fitted into the frame's maximum size
+ */
+function fitItemsBestIntoFrame(board: Whiteboard): Whiteboard {
     const neoboardPadding = 10;
 
-    // The first step is to find the bounding box of all the slides.
-    let minX = Number.MAX_SAFE_INTEGER;
-    let minY = Number.MAX_SAFE_INTEGER;
-    let maxX = Number.MIN_SAFE_INTEGER;
-    let maxY = Number.MIN_SAFE_INTEGER;
+    const transformedBoard: Whiteboard = {
+        version: board.version,
+        whiteboard: {
+            slides: [],
+        }
+    };
+
+    // 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;
@@ -216,39 +237,29 @@ function transformBoardIntoNeoboardCoordinates(board: Whiteboard): Whiteboard {
                 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, ...points.map(p => p.x));
-                minY = Math.min(minY, y, ...points.map(p => p.y));
-                maxX = Math.max(maxX, x, ...points.map(p => p.x));
-                maxY = Math.max(maxY, y, ...points.map(p => p.y));
+                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)));
             }
         }
-    }
-
-    // The second step is to scale all the elements to fit within the bounding box.
-    const width = maxX - minX;
-    const height = maxY - minY;
-    const scale = Math.min((neoboardWhiteboardWidth - neoboardPadding) / width, (neoboardWhiteboardHeight - neoboardPadding) / height);
-
-    const transformedBoard: Whiteboard = {
-        version: board.version,
-        whiteboard: {
-            slides: [],
-        }
-    };
 
-    for (const slide of board.whiteboard.slides) {
-        const transformedSlide: Slide = {
-            elements: [],
-        };
+        // 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;
-                const y = (element.position.y - minY) * scale;
+                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;
 
@@ -262,11 +273,12 @@ function transformBoardIntoNeoboardCoordinates(board: Whiteboard): Whiteboard {
                     height,
                 });
             } else if (element.type === 'path') {
-                const x = (element.position.x - minX) * scale;
-                const y = (element.position.y - minY) * scale;
+                // 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 - minX) * scale,
-                    y: (p.y - minY) * scale,
+                    x: p.x * scale,
+                    y: p.y * scale,
                 }));
 
                 transformedSlide.elements.push({
@@ -277,7 +289,6 @@ function transformBoardIntoNeoboardCoordinates(board: Whiteboard): Whiteboard {
                     },
                     points,
                 });
-
             } else {
                 transformedSlide.elements.push(element);
             }