From 517befe2ccf0952e7757de7a292a151fcb26236f Mon Sep 17 00:00:00 2001 From: Peng Xiao Date: Thu, 18 Aug 2022 12:52:26 +0800 Subject: [PATCH] fix: rotate + resizing issue --- .../TLSelectTool/states/ResizingState.ts | 46 ++++++++++--------- tldraw/packages/core/src/utils/BoundsUtils.ts | 44 ++++++++++++++---- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/tldraw/packages/core/src/lib/tools/TLSelectTool/states/ResizingState.ts b/tldraw/packages/core/src/lib/tools/TLSelectTool/states/ResizingState.ts index e520453bcd..95e47e9035 100644 --- a/tldraw/packages/core/src/lib/tools/TLSelectTool/states/ResizingState.ts +++ b/tldraw/packages/core/src/lib/tools/TLSelectTool/states/ResizingState.ts @@ -184,28 +184,30 @@ export class ResizingState< rotation *= -1 } // If the shape is aspect ratio locked or size locked... - if (isAspectRatioLocked || !canResizeAny || shape.props.isSizeLocked) { - relativeBounds.width = initialShapeBounds.width - relativeBounds.height = initialShapeBounds.height - if (isAspectRatioLocked) { - // Scale the width and height to the longer dimension - relativeBounds.width *= resizeDimension - relativeBounds.height *= resizeDimension - } - // Find the center using the inner transform origin - center = [ - nextBounds.minX + - (scaleX < 0 ? 1 - innerTransformOrigin[0] : innerTransformOrigin[0]) * - (nextBounds.width - relativeBounds.width) + - relativeBounds.width / 2, - nextBounds.minY + - (scaleY < 0 ? 1 - innerTransformOrigin[1] : innerTransformOrigin[1]) * - (nextBounds.height - relativeBounds.height) + - relativeBounds.height / 2, - ] - // Position the bounds at the center - relativeBounds = BoundsUtils.centerBounds(relativeBounds, center) - } + // FIXME: the following is buggy + // if (isAspectRatioLocked || !canResizeAny || shape.props.isSizeLocked) { + // // console.log('aspect ratio locked', isAspectRatioLocked, canResizeAny, shape.props.isSizeLocked) + // relativeBounds.width = initialShapeBounds.width + // relativeBounds.height = initialShapeBounds.height + // if (isAspectRatioLocked) { + // // Scale the width and height to the longer dimension + // relativeBounds.width *= resizeDimension + // relativeBounds.height *= resizeDimension + // } + // // Find the center using the inner transform origin + // center = [ + // nextBounds.minX + + // (scaleX < 0 ? 1 - innerTransformOrigin[0] : innerTransformOrigin[0]) * + // (nextBounds.width - relativeBounds.width) + + // relativeBounds.width / 2, + // nextBounds.minY + + // (scaleY < 0 ? 1 - innerTransformOrigin[1] : innerTransformOrigin[1]) * + // (nextBounds.height - relativeBounds.height) + + // relativeBounds.height / 2, + // ] + // // Position the bounds at the center + // relativeBounds = BoundsUtils.centerBounds(relativeBounds, center) + // } shape.onResize(initialShapeProps, { center, rotation, diff --git a/tldraw/packages/core/src/utils/BoundsUtils.ts b/tldraw/packages/core/src/utils/BoundsUtils.ts index 992dea9300..8461cb96d3 100644 --- a/tldraw/packages/core/src/utils/BoundsUtils.ts +++ b/tldraw/packages/core/src/utils/BoundsUtils.ts @@ -323,15 +323,16 @@ export class BoundsUtils { handle: TLResizeCorner | TLResizeEdge | 'center', delta: number[], rotation = 0, - isAspectRatioLocked = false, - [canResizeX, canResizeY] = [true, true] + isAspectRatioLocked = false ): TLBounds & { scaleX: number; scaleY: number } { // Create top left and bottom right corners. const [ax0, ay0] = [bounds.minX, bounds.minY] const [ax1, ay1] = [bounds.maxX, bounds.maxY] + // Create a second set of corners for the new box. let [bx0, by0] = [bounds.minX, bounds.minY] let [bx1, by1] = [bounds.maxX, bounds.maxY] + // If the drag is on the center, just translate the bounds. if (handle === 'center') { return { @@ -345,11 +346,14 @@ export class BoundsUtils { scaleY: 1, } } + // Counter rotate the delta. This lets us make changes as if // the (possibly rotated) boxes were axis aligned. const [dx, dy] = Vec.rot(delta, -rotation) + /* 1. Delta + Use the delta to adjust the new box by changing its corners. The dragging handle (corner or edge) will determine which corners should change. @@ -368,6 +372,7 @@ corners should change. break } } + switch (handle) { case TLResizeEdge.Left: case TLResizeCorner.TopLeft: @@ -382,24 +387,32 @@ corners should change. break } } + const aw = ax1 - ax0 const ah = ay1 - ay0 + const scaleX = (bx1 - bx0) / aw const scaleY = (by1 - by0) / ah + const flipX = scaleX < 0 const flipY = scaleY < 0 + const bw = Math.abs(bx1 - bx0) const bh = Math.abs(by1 - by0) + /* - 2. Aspect ratio - If the aspect ratio is locked, adjust the corners so that the - new box's aspect ratio matches the original aspect ratio. - */ +2. Aspect ratio + +If the aspect ratio is locked, adjust the corners so that the +new box's aspect ratio matches the original aspect ratio. +*/ + if (isAspectRatioLocked) { const ar = aw / ah const isTall = ar < bw / bh const tw = bw * (scaleY < 0 ? 1 : -1) * (1 / ar) const th = bh * (scaleX < 0 ? 1 : -1) * ar + switch (handle) { case TLResizeCorner.TopLeft: { if (isTall) by0 = by1 + tw @@ -439,17 +452,22 @@ corners should change. } } } + /* 3. Rotation + If the bounds are rotated, get a Vector from the rotated anchor corner in the inital bounds to the rotated anchor corner in the result's bounds. Subtract this Vector from the result's corners, so that the two anchor points (initial and result) will be equal. */ + if (rotation % (Math.PI * 2) !== 0) { let cv = [0, 0] + const c0 = Vec.med([ax0, ay0], [ax1, ay1]) const c1 = Vec.med([bx0, by0], [bx1, by1]) + switch (handle) { case TLResizeCorner.TopLeft: { cv = Vec.sub(Vec.rotWith([bx1, by1], c1, rotation), Vec.rotWith([ax1, ay1], c0, rotation)) @@ -496,16 +514,26 @@ so that the two anchor points (initial and result) will be equal. break } } + ;[bx0, by0] = Vec.sub([bx0, by0], cv) ;[bx1, by1] = Vec.sub([bx1, by1], cv) } + /* 4. Flips + If the axes are flipped (e.g. if the right edge has been dragged left past the initial left edge) then swap points on that axis. */ - if (bx1 < bx0) [bx1, bx0] = [bx0, bx1] - if (by1 < by0) [by1, by0] = [by0, by1] + + if (bx1 < bx0) { + ;[bx1, bx0] = [bx0, bx1] + } + + if (by1 < by0) { + ;[by1, by0] = [by0, by1] + } + return { minX: bx0, minY: by0,