From dedfd2d016ec6b11537c599d4409b3dbf0cdba9d Mon Sep 17 00:00:00 2001 From: MTRNord Date: Sun, 12 Jan 2025 00:12:25 +0100 Subject: [PATCH] Improve the coordinate system transformation to actually reflect the correct coordinates Change-Id: I31629b109236e6f22500332cef99ff32b86663c3 --- src/app/importActions.ts | 176 ++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 86 deletions(-) diff --git a/src/app/importActions.ts b/src/app/importActions.ts index 6033f04..0fbcbfc 100644 --- a/src/app/importActions.ts +++ b/src/app/importActions.ts @@ -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); } } -- 2.45.2