diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts index a4a357d9d..84c67276b 100644 --- a/app/api/canvas/local-edit/route.ts +++ b/app/api/canvas/local-edit/route.ts @@ -425,6 +425,41 @@ function expandRectWithMargins( }; } +function getRectUnion( + leftRect: NormalizedLocalEditRegion["rect"], + rightRect: NormalizedLocalEditRegion["rect"] +) { + const left = Math.min(leftRect.x, rightRect.x); + const top = Math.min(leftRect.y, rightRect.y); + const right = Math.max( + leftRect.x + leftRect.width, + rightRect.x + rightRect.width + ); + const bottom = Math.max( + leftRect.y + leftRect.height, + rightRect.y + rightRect.height + ); + return { + x: clamp(left, 0, 1), + y: clamp(top, 0, 1), + width: Math.max(0.001, clamp(right, 0, 1) - clamp(left, 0, 1)), + height: Math.max(0.001, clamp(bottom, 0, 1) - clamp(top, 0, 1)), + }; +} + +function rectsDifferMeaningfully( + leftRect: NormalizedLocalEditRegion["rect"], + rightRect: NormalizedLocalEditRegion["rect"], + epsilon = 0.0005 +) { + return ( + Math.abs(leftRect.x - rightRect.x) > epsilon || + Math.abs(leftRect.y - rightRect.y) > epsilon || + Math.abs(leftRect.width - rightRect.width) > epsilon || + Math.abs(leftRect.height - rightRect.height) > epsilon + ); +} + function getScaleAdjustWorkspaceRect( sourceRect: NormalizedLocalEditRegion["rect"], targetRect: NormalizedLocalEditRegion["rect"], @@ -432,37 +467,55 @@ function getScaleAdjustWorkspaceRect( ) { const sourceWidth = Math.max(0.0001, sourceRect.width); const sourceHeight = Math.max(0.0001, sourceRect.height); - const widthGrowthRatio = Math.max(0, targetRect.width / sourceWidth - 1); - const heightGrowthRatio = Math.max(0, targetRect.height / sourceHeight - 1); - const areaGrowthRatio = Math.max( - 0, + const widthDeltaRatio = Math.abs(targetRect.width / sourceWidth - 1); + const heightDeltaRatio = Math.abs(targetRect.height / sourceHeight - 1); + const areaDeltaRatio = Math.abs( Math.sqrt( Math.max(0.0001, targetRect.width * targetRect.height) / Math.max(0.0001, sourceWidth * sourceHeight) ) - 1 ); - const explicitGrowthRatio = Math.max( - widthGrowthRatio, - heightGrowthRatio, - areaGrowthRatio + const sourceCenterX = sourceRect.x + sourceRect.width / 2; + const sourceCenterY = sourceRect.y + sourceRect.height / 2; + const targetCenterX = targetRect.x + targetRect.width / 2; + const targetCenterY = targetRect.y + targetRect.height / 2; + const movementRatioX = Math.abs(targetCenterX - sourceCenterX) / sourceWidth; + const movementRatioY = Math.abs(targetCenterY - sourceCenterY) / sourceHeight; + const explicitChangeRatio = Math.max( + widthDeltaRatio, + heightDeltaRatio, + areaDeltaRatio, + movementRatioX, + movementRatioY ); const fallbackScaleRatio = typeof scalePercent === "number" && Number.isFinite(scalePercent) - ? Math.min(1, Math.max(0, scalePercent / 100)) + ? Math.min(1, Math.abs(scalePercent) / 100) : 0; - const scaleRatio = Math.max(explicitGrowthRatio, fallbackScaleRatio); - if (scaleRatio <= 0) return targetRect; + const changeRatio = Math.max(explicitChangeRatio, fallbackScaleRatio); + const unionRect = getRectUnion(sourceRect, targetRect); + if (changeRatio <= 0 && !rectsDifferMeaningfully(unionRect, targetRect)) { + return targetRect; + } const marginX = Math.min( - 0.12, - Math.max(0.018, sourceRect.width * scaleRatio * 0.22) + 0.16, + Math.max( + 0.018, + unionRect.width * 0.04, + sourceRect.width * changeRatio * 0.24 + ) ); const marginY = Math.min( - 0.12, - Math.max(0.018, sourceRect.height * scaleRatio * 0.22) + 0.16, + Math.max( + 0.018, + unionRect.height * 0.04, + sourceRect.height * changeRatio * 0.24 + ) ); - return expandRectWithMargins(targetRect, marginX, marginY); + return expandRectWithMargins(unionRect, marginX, marginY); } function buildScaleAdjustCompositeRegions( @@ -476,8 +529,8 @@ function buildScaleAdjustCompositeRegions( targetRect, scalePercent ); - return workspaceRect.width > targetRect.width || - workspaceRect.height > targetRect.height + return rectsDifferMeaningfully(workspaceRect, targetRect) && + rectsDifferMeaningfully(workspaceRect, region.rect) ? [region.rect, targetRect, workspaceRect] : [region.rect, targetRect]; });