From ee703206b067383977891c63b906767d1946404b Mon Sep 17 00:00:00 2001 From: Aakansha Doshi Date: Sat, 16 Jan 2021 22:20:46 +0530 Subject: [PATCH 1/3] docs: update changelog and release 0.2.0 (#2725) * docs(changelog): update changelog and release 0.2.0 * fix * Add missing API's * update * fix * fix * Apply suggestions from code review * remove * fix * fix --- src/packages/excalidraw/CHANGELOG.md | 26 ++++++++++++- src/packages/excalidraw/README.md | 45 +++++++++++++++++++++++ src/packages/excalidraw/package-lock.json | 2 +- src/packages/excalidraw/package.json | 2 +- 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index aa0ec43ba5..f161523892 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -12,15 +12,21 @@ The change should be grouped under one of the below section and must contain PR Please add the latest change on the top under the correct section. --> -## [Unreleased] +## 0.2.0 ### Features +- Add toast [#2772](https://github.com/excalidraw/excalidraw/pull/2772) - Add `cmd+o` shortcut to load scene [#2732](https://github.com/excalidraw/excalidraw/pull/2732) - Remove language picker, and add `langCode`, `renderFooter` [#2644](https://github.com/excalidraw/excalidraw/pull/2644): - BREAKING: removed the language picker from UI. It is now the host app's responsibility to implement a language picker if desirable, using the newly added [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. The reasoning is that the i18n should be controlled by the app itself, not by the nested Excalidraw component. - Added [`langCode`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#langCode) prop to control the UI language. - Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files) +- Require use of a preset dialog size; adjust dialog sizing [#2684](https://github.com/excalidraw/excalidraw/pull/2684) +- Add line chart and paste dialog selection [#2670](https://github.com/excalidraw/excalidraw/pull/2670) +- Tweak editing behavior [#2668](https://github.com/excalidraw/excalidraw/pull/2668) +- Change title to Excalidraw after a timeout +- Checkmark to toggle context-menu-items [#2645](https://github.com/excalidraw/excalidraw/pull/2645) - Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522) - Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527) - Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501) @@ -31,7 +37,12 @@ Please add the latest change on the top under the correct section. ### Fixes +- Fix typo for initialData and point all links to master [#2707](https://github.com/excalidraw/excalidraw/pull/2707) +- Fix compile error [#2685](https://github.com/excalidraw/excalidraw/pull/2685) +- Center zoom on iPhone and iPad [#2642](https://github.com/excalidraw/excalidraw/pull/2642) - Allow text-selecting in dialogs & reset cursor [#2783](https://github.com/excalidraw/excalidraw/pull/2783) +- Don't render due to zoom after unmount [#2779](https://github.com/excalidraw/excalidraw/pull/2779) +- Track the chart type correctly [#2773](https://github.com/excalidraw/excalidraw/pull/2773) - Fix late-render due to debounced zoom [#2779](https://github.com/excalidraw/excalidraw/pull/2779) - Fix initialization when browser tab not focused [#2677](https://github.com/excalidraw/excalidraw/pull/2677) - Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622) @@ -47,6 +58,11 @@ Please add the latest change on the top under the correct section. ### Improvements - Added Zen Mode to the context menu [#2734](https://github.com/excalidraw/excalidraw/pull/2734) +- Do not reset to selection for draw tool [#2721]((https://github.com/excalidraw/excalidraw/pull/2721) +- Make dialogs look more like dialogs [#2686](https://github.com/excalidraw/excalidraw/pull/2686) +- Browse libraries styles fixed [#2694](https://github.com/excalidraw/excalidraw/pull/2694) +- Change hint for 2-point lines on resize [#2655](https://github.com/excalidraw/excalidraw/pull/2655) +- Align items in context menu [#2640](https://github.com/excalidraw/excalidraw/pull/2640) - Do not reset to selection when using the draw tool [#2721](https://github.com/excalidraw/excalidraw/pull/2721) - Display proper tooltip for 2-point lines during resize, and normalize modifier key labels in hints [#2655](https://github.com/excalidraw/excalidraw/pull/2655) - Improve error message around importing images [#2619](https://github.com/excalidraw/excalidraw/pull/2619) @@ -56,6 +72,14 @@ Please add the latest change on the top under the correct section. - Hide shortcuts on pickers for mobile [#2508](https://github.com/excalidraw/excalidraw/pull/2508) - Hide stats and scrollToContent-button when mobile menus open [#2509](https://github.com/excalidraw/excalidraw/pull/2509) +### Refactor + +- refactor: Converting span to kbd tag [#2774](https://github.com/excalidraw/excalidraw/pull/2774) +- Media queries [#2680](https://github.com/excalidraw/excalidraw/pull/2680) +- Remove duplicate entry from en.json[#2654](https://github.com/excalidraw/excalidraw/pull/2654) +- Remove the word toggle from labels [#2648](https://github.com/excalidraw/excalidraw/pull/2648) +- + ### Chore - Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500) diff --git a/src/packages/excalidraw/README.md b/src/packages/excalidraw/README.md index 36c76f6687..12adfb6bfb 100644 --- a/src/packages/excalidraw/README.md +++ b/src/packages/excalidraw/README.md @@ -142,6 +142,51 @@ export default function App() { | [`langCode`](#langCode) | string | `en` | Language code string | | [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer | +### `Extra API's` + +#### `getSceneVersion` + +**How to use** + +
+import { getSceneVersion } from "@excalidraw/excalidraw";
+getSceneVersion(elements:  ExcalidrawElement [])
+
+ +This function returns the current scene version. + +#### `getSyncableElements` + +**_Signature_** + +
+getSyncableElements(elements:  ExcalidrawElement []):ExcalidrawElement []
+
+ +**How to use** + +```js +import { getSyncableElements } from "@excalidraw/excalidraw"; +``` + +This function returns all the deleted elements of the scene. + +### `getElementMap` + +**_Signature_** + +
+getElementsMap(elements:  ExcalidrawElement []): {[id: string]: ExcalidrawElement}
+
+ +**How to use** + +```js +import { getElementsMap } from "@excalidraw/excalidraw"; +``` + +This function returns an object where each element is mapped to its id. + #### `width` This props defines the `width` of the Excalidraw component. Defaults to `window.innerWidth` if not passed. diff --git a/src/packages/excalidraw/package-lock.json b/src/packages/excalidraw/package-lock.json index 9e2c3df72f..2b5f7c7569 100644 --- a/src/packages/excalidraw/package-lock.json +++ b/src/packages/excalidraw/package-lock.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.1.1", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json index 62125fe9f9..8c0a529abb 100644 --- a/src/packages/excalidraw/package.json +++ b/src/packages/excalidraw/package.json @@ -1,6 +1,6 @@ { "name": "@excalidraw/excalidraw", - "version": "0.1.1", + "version": "0.2.0", "main": "dist/excalidraw.min.js", "files": [ "dist/*" From c799b28a0ed3ac4b3643cd86b66ef5c72a7d0d3f Mon Sep 17 00:00:00 2001 From: Lipis Date: Sat, 16 Jan 2021 19:59:26 +0200 Subject: [PATCH 2/3] fix: Count all versions (#2798) --- src/constants.ts | 1 + src/excalidraw-app/index.tsx | 16 ++++------------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index b577da57a6..f783bb5fa5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -90,3 +90,4 @@ export const TAP_TWICE_TIMEOUT = 300; export const TOUCH_CTX_MENU_TIMEOUT = 500; export const TITLE_TIMEOUT = 10000; export const TOAST_TIMEOUT = 5000; +export const VERSION_TIMEOUT = 15000; diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx index 9dbdf544fa..e90664e8e5 100644 --- a/src/excalidraw-app/index.tsx +++ b/src/excalidraw-app/index.tsx @@ -11,7 +11,7 @@ import { getDefaultAppState } from "../appState"; import { ExcalidrawImperativeAPI } from "../components/App"; import { ErrorDialog } from "../components/ErrorDialog"; import { TopErrorBoundary } from "../components/TopErrorBoundary"; -import { APP_NAME, EVENT, TITLE_TIMEOUT } from "../constants"; +import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants"; import { ImportedDataState } from "../data/types"; import { ExcalidrawElement, @@ -229,18 +229,10 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) { const { collab } = props; useEffect(() => { - // delayed by 15 sec so that the app has a time to load the latest SW + // Delayed so that the app has a time to load the latest SW setTimeout(() => { - const version = getVersion(); - const loggedVersion = window.localStorage.getItem( - "excalidraw-lastLoggedVersion", - ); - // prevent logging on multiple visits - if (version && version !== loggedVersion) { - window.localStorage.setItem("excalidraw-lastLoggedVersion", version); - trackEvent("load", "version", version); - } - }, 15000); + trackEvent("load", "version", getVersion()); + }, VERSION_TIMEOUT); excalidrawRef.current!.readyPromise.then((excalidrawApi) => { initializeScene({ From e26f374ca6175d70d182e9353dae209b71f28e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Forja?= Date: Sat, 16 Jan 2021 18:49:13 +0000 Subject: [PATCH 3/3] improvement: Enhance resize for non generic elements (#2720) --- src/components/App.tsx | 3 - src/element/index.ts | 1 - src/element/resizeElements.ts | 317 ++++++++++++---------------------- src/element/resizeTest.ts | 54 ------ src/math.ts | 58 ------- src/tests/resize.test.tsx | 2 +- 6 files changed, 112 insertions(+), 323 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index faca963c6f..d680034c5f 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -3587,9 +3587,6 @@ class App extends React.Component { transformElements( pointerDownState, transformHandleType, - (newTransformHandle) => { - pointerDownState.resize.handleType = newTransformHandle; - }, selectedElements, pointerDownState.resize.arrowDirection, getRotateWithDiscreteAngleKey(event), diff --git a/src/element/index.ts b/src/element/index.ts index e49bc633e3..63fdcfff49 100644 --- a/src/element/index.ts +++ b/src/element/index.ts @@ -34,7 +34,6 @@ export { export { resizeTest, getCursorForResizingElement, - normalizeTransformHandleType, getElementWithTransformHandleType, getTransformHandleTypeFromCoords, } from "./resizeTest"; diff --git a/src/element/resizeElements.ts b/src/element/resizeElements.ts index 199e3a4824..96c216b06e 100644 --- a/src/element/resizeElements.ts +++ b/src/element/resizeElements.ts @@ -4,7 +4,6 @@ import { rescalePoints } from "../points"; import { rotate, adjustXYWithRotation, - getFlipAdjustment, centerPoint, rotatePoint, } from "../math"; @@ -13,21 +12,16 @@ import { ExcalidrawTextElement, NonDeletedExcalidrawElement, NonDeleted, - ExcalidrawGenericElement, - ExcalidrawElement, } from "./types"; import { getElementAbsoluteCoords, getCommonBounds, getResizedElementAbsoluteCoords, } from "./bounds"; -import { isGenericElement, isLinearElement, isTextElement } from "./typeChecks"; +import { isLinearElement, isTextElement } from "./typeChecks"; import { mutateElement } from "./mutateElement"; import { getPerfectElementSize } from "./sizeHelpers"; -import { - getCursorForResizingElement, - normalizeTransformHandleType, -} from "./resizeTest"; +import { getCursorForResizingElement } from "./resizeTest"; import { measureText, getFontString } from "../utils"; import { updateBoundElements } from "./binding"; import { @@ -49,7 +43,6 @@ const normalizeAngle = (angle: number): number => { export const transformElements = ( pointerDownState: PointerDownState, transformHandleType: MaybeTransformHandleType, - setTransformHandle: (nextTransformHandle: MaybeTransformHandleType) => void, selectedElements: readonly NonDeletedExcalidrawElement[], resizeArrowDirection: "origin" | "end", isRotateWithDiscreteAngle: boolean, @@ -101,36 +94,15 @@ export const transformElements = ( ); updateBoundElements(element); } else if (transformHandleType) { - if (isGenericElement(element)) { - resizeSingleGenericElement( - pointerDownState.originalElements.get(element.id) as typeof element, - shouldKeepSidesRatio, - element, - transformHandleType, - isResizeCenterPoint, - pointerX, - pointerY, - ); - } else { - const keepSquareAspectRatio = shouldKeepSidesRatio; - resizeSingleNonGenericElement( - element, - transformHandleType, - isResizeCenterPoint, - keepSquareAspectRatio, - pointerX, - pointerY, - ); - setTransformHandle( - normalizeTransformHandleType(element, transformHandleType), - ); - if (element.width < 0) { - mutateElement(element, { width: -element.width }); - } - if (element.height < 0) { - mutateElement(element, { height: -element.height }); - } - } + resizeSingleElement( + pointerDownState.originalElements.get(element.id) as typeof element, + shouldKeepSidesRatio, + element, + transformHandleType, + isResizeCenterPoint, + pointerX, + pointerY, + ); } // update cursor @@ -414,8 +386,8 @@ const resizeSingleTextElement = ( } }; -const resizeSingleGenericElement = ( - stateAtResizeStart: NonDeleted, +const resizeSingleElement = ( + stateAtResizeStart: NonDeletedExcalidrawElement, shouldKeepSidesRatio: boolean, element: NonDeletedExcalidrawElement, transformHandleDirection: TransformHandleDirection, @@ -423,251 +395,184 @@ const resizeSingleGenericElement = ( pointerX: number, pointerY: number, ) => { - const [x1, y1, x2, y2] = getElementAbsoluteCoords(stateAtResizeStart); + // Gets bounds corners + const [x1, y1, x2, y2] = getResizedElementAbsoluteCoords( + stateAtResizeStart, + stateAtResizeStart.width, + stateAtResizeStart.height, + ); const startTopLeft: Point = [x1, y1]; const startBottomRight: Point = [x2, y2]; const startCenter: Point = centerPoint(startTopLeft, startBottomRight); // Calculate new dimensions based on cursor position - let newWidth = stateAtResizeStart.width; - let newHeight = stateAtResizeStart.height; const rotatedPointer = rotatePoint( [pointerX, pointerY], startCenter, -stateAtResizeStart.angle, ); + + //Get bounds corners rendered on screen + const [esx1, esy1, esx2, esy2] = getResizedElementAbsoluteCoords( + element, + element.width, + element.height, + ); + const boundsCurrentWidth = esx2 - esx1; + const boundsCurrentHeight = esy2 - esy1; + + // It's important we set the initial scale value based on the width and height at resize start, + // otherwise previous dimensions affected by modifiers will be taken into account. + const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0]; + const atStartBoundsHeight = startBottomRight[1] - startTopLeft[1]; + let scaleX = atStartBoundsWidth / boundsCurrentWidth; + let scaleY = atStartBoundsHeight / boundsCurrentHeight; + if (transformHandleDirection.includes("e")) { - newWidth = rotatedPointer[0] - startTopLeft[0]; + scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth; } if (transformHandleDirection.includes("s")) { - newHeight = rotatedPointer[1] - startTopLeft[1]; + scaleY = (rotatedPointer[1] - startTopLeft[1]) / boundsCurrentHeight; } if (transformHandleDirection.includes("w")) { - newWidth = startBottomRight[0] - rotatedPointer[0]; + scaleX = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth; } if (transformHandleDirection.includes("n")) { - newHeight = startBottomRight[1] - rotatedPointer[1]; + scaleY = (startBottomRight[1] - rotatedPointer[1]) / boundsCurrentHeight; } + // Linear elements dimensions differ from bounds dimensions + const eleInitialWidth = stateAtResizeStart.width; + const eleInitialHeight = stateAtResizeStart.height; + // We have to use dimensions of element on screen, otherwise the scaling of the + // dimensions won't match the cursor for linear elements. + let eleNewWidth = element.width * scaleX; + let eleNewHeight = element.height * scaleY; // adjust dimensions for resizing from center if (isResizeFromCenter) { - newWidth = 2 * newWidth - stateAtResizeStart.width; - newHeight = 2 * newHeight - stateAtResizeStart.height; + eleNewWidth = 2 * eleNewWidth - eleInitialWidth; + eleNewHeight = 2 * eleNewHeight - eleInitialHeight; } // adjust dimensions to keep sides ratio if (shouldKeepSidesRatio) { - const widthRatio = Math.abs(newWidth) / stateAtResizeStart.width; - const heightRatio = Math.abs(newHeight) / stateAtResizeStart.height; + const widthRatio = Math.abs(eleNewWidth) / eleInitialWidth; + const heightRatio = Math.abs(eleNewHeight) / eleInitialHeight; if (transformHandleDirection.length === 1) { - newHeight *= widthRatio; - newWidth *= heightRatio; + eleNewHeight *= widthRatio; + eleNewWidth *= heightRatio; } if (transformHandleDirection.length === 2) { const ratio = Math.max(widthRatio, heightRatio); - newWidth = stateAtResizeStart.width * ratio * Math.sign(newWidth); - newHeight = stateAtResizeStart.height * ratio * Math.sign(newHeight); + eleNewWidth = eleInitialWidth * ratio * Math.sign(eleNewWidth); + eleNewHeight = eleInitialHeight * ratio * Math.sign(eleNewHeight); } } + const [ + newBoundsX1, + newBoundsY1, + newBoundsX2, + newBoundsY2, + ] = getResizedElementAbsoluteCoords( + stateAtResizeStart, + eleNewWidth, + eleNewHeight, + ); + const newBoundsWidth = newBoundsX2 - newBoundsX1; + const newBoundsHeight = newBoundsY2 - newBoundsY1; + // Calculate new topLeft based on fixed corner during resize - let newTopLeft = startTopLeft as [number, number]; + let newTopLeft = [...startTopLeft] as [number, number]; if (["n", "w", "nw"].includes(transformHandleDirection)) { newTopLeft = [ - startBottomRight[0] - Math.abs(newWidth), - startBottomRight[1] - Math.abs(newHeight), + startBottomRight[0] - Math.abs(newBoundsWidth), + startBottomRight[1] - Math.abs(newBoundsHeight), ]; } if (transformHandleDirection === "ne") { - const bottomLeft = [ - stateAtResizeStart.x, - stateAtResizeStart.y + stateAtResizeStart.height, - ]; - newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newHeight)]; + const bottomLeft = [startTopLeft[0], startBottomRight[1]]; + newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newBoundsHeight)]; } if (transformHandleDirection === "sw") { - const topRight = [ - stateAtResizeStart.x + stateAtResizeStart.width, - stateAtResizeStart.y, - ]; - newTopLeft = [topRight[0] - Math.abs(newWidth), topRight[1]]; + const topRight = [startBottomRight[0], startTopLeft[1]]; + newTopLeft = [topRight[0] - Math.abs(newBoundsWidth), topRight[1]]; } // Keeps opposite handle fixed during resize if (shouldKeepSidesRatio) { if (["s", "n"].includes(transformHandleDirection)) { - newTopLeft[0] = startCenter[0] - newWidth / 2; + newTopLeft[0] = startCenter[0] - newBoundsWidth / 2; } if (["e", "w"].includes(transformHandleDirection)) { - newTopLeft[1] = startCenter[1] - newHeight / 2; + newTopLeft[1] = startCenter[1] - newBoundsHeight / 2; } } // Flip horizontally - if (newWidth < 0) { + if (eleNewWidth < 0) { if (transformHandleDirection.includes("e")) { - newTopLeft[0] -= Math.abs(newWidth); + newTopLeft[0] -= Math.abs(newBoundsWidth); } if (transformHandleDirection.includes("w")) { - newTopLeft[0] += Math.abs(newWidth); + newTopLeft[0] += Math.abs(newBoundsWidth); } } // Flip vertically - if (newHeight < 0) { + if (eleNewHeight < 0) { if (transformHandleDirection.includes("s")) { - newTopLeft[1] -= Math.abs(newHeight); + newTopLeft[1] -= Math.abs(newBoundsHeight); } if (transformHandleDirection.includes("n")) { - newTopLeft[1] += Math.abs(newHeight); + newTopLeft[1] += Math.abs(newBoundsHeight); } } if (isResizeFromCenter) { - newTopLeft[0] = startCenter[0] - Math.abs(newWidth) / 2; - newTopLeft[1] = startCenter[1] - Math.abs(newHeight) / 2; + newTopLeft[0] = startCenter[0] - Math.abs(newBoundsWidth) / 2; + newTopLeft[1] = startCenter[1] - Math.abs(newBoundsHeight) / 2; } // adjust topLeft to new rotation point const angle = stateAtResizeStart.angle; const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle); const newCenter: Point = [ - newTopLeft[0] + Math.abs(newWidth) / 2, - newTopLeft[1] + Math.abs(newHeight) / 2, + newTopLeft[0] + Math.abs(newBoundsWidth) / 2, + newTopLeft[1] + Math.abs(newBoundsHeight) / 2, ]; const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle); newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle); - const resizedElement = { - width: Math.abs(newWidth), - height: Math.abs(newHeight), - x: newTopLeft[0], - y: newTopLeft[1], - }; - updateBoundElements(element, { - newSize: { width: resizedElement.width, height: resizedElement.height }, - }); - mutateElement(element, resizedElement); -}; - -const resizeSingleNonGenericElement = ( - element: NonDeleted>, - transformHandleType: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se", - isResizeFromCenter: boolean, - keepSquareAspectRatio: boolean, - pointerX: number, - pointerY: number, -) => { - const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); - const cx = (x1 + x2) / 2; - const cy = (y1 + y2) / 2; - - // rotation pointer with reverse angle - const [rotatedX, rotatedY] = rotate( - pointerX, - pointerY, - cx, - cy, - -element.angle, + // Readjust points for linear elements + const rescaledPoints = rescalePointsInElement( + stateAtResizeStart, + eleNewWidth, + eleNewHeight, ); + // For linear elements (x,y) are the coordinates of the first drawn point not the top-left corner + // So we need to readjust (x,y) to be where the first point should be + const newOrigin = [...newTopLeft]; + newOrigin[0] += stateAtResizeStart.x - newBoundsX1; + newOrigin[1] += stateAtResizeStart.y - newBoundsY1; - let scaleX = 1; - let scaleY = 1; - if ( - transformHandleType === "e" || - transformHandleType === "ne" || - transformHandleType === "se" - ) { - scaleX = (rotatedX - x1) / (x2 - x1); - } - if ( - transformHandleType === "s" || - transformHandleType === "sw" || - transformHandleType === "se" - ) { - scaleY = (rotatedY - y1) / (y2 - y1); - } - if ( - transformHandleType === "w" || - transformHandleType === "nw" || - transformHandleType === "sw" - ) { - scaleX = (x2 - rotatedX) / (x2 - x1); - } - if ( - transformHandleType === "n" || - transformHandleType === "nw" || - transformHandleType === "ne" - ) { - scaleY = (y2 - rotatedY) / (y2 - y1); - } - let nextWidth = element.width * scaleX; - let nextHeight = element.height * scaleY; - if (keepSquareAspectRatio) { - nextWidth = nextHeight = Math.max(nextWidth, nextHeight); - } - - const [nextX1, nextY1, nextX2, nextY2] = getResizedElementAbsoluteCoords( - element, - nextWidth, - nextHeight, - ); - const deltaX1 = (x1 - nextX1) / 2; - const deltaY1 = (y1 - nextY1) / 2; - const deltaX2 = (x2 - nextX2) / 2; - const deltaY2 = (y2 - nextY2) / 2; - - const rescaledPoints = rescalePointsInElement(element, nextWidth, nextHeight); - - updateBoundElements(element, { - newSize: { width: nextWidth, height: nextHeight }, - }); - const [finalX1, finalY1, finalX2, finalY2] = getResizedElementAbsoluteCoords( - { - ...element, - ...rescaledPoints, - }, - Math.abs(nextWidth), - Math.abs(nextHeight), - ); - const [flipDiffX, flipDiffY] = getFlipAdjustment( - transformHandleType, - nextWidth, - nextHeight, - nextX1, - nextY1, - nextX2, - nextY2, - finalX1, - finalY1, - finalX2, - finalY2, - isLinearElement(element), - element.angle, - ); - const [nextElementX, nextElementY] = adjustXYWithRotation( - getSidesForTransformHandle(transformHandleType, isResizeFromCenter), - element.x - flipDiffX, - element.y - flipDiffY, - element.angle, - deltaX1, - deltaY1, - deltaX2, - deltaY2, - ); + const resizedElement = { + width: Math.abs(eleNewWidth), + height: Math.abs(eleNewHeight), + x: newOrigin[0], + y: newOrigin[1], + ...rescaledPoints, + }; if ( - nextWidth !== 0 && - nextHeight !== 0 && - Number.isFinite(nextElementX) && - Number.isFinite(nextElementY) + resizedElement.width !== 0 && + resizedElement.height !== 0 && + Number.isFinite(resizedElement.x) && + Number.isFinite(resizedElement.y) ) { - mutateElement(element, { - width: nextWidth, - height: nextHeight, - x: nextElementX, - y: nextElementY, - ...rescaledPoints, + updateBoundElements(element, { + newSize: { width: resizedElement.width, height: resizedElement.height }, }); + mutateElement(element, resizedElement); } }; diff --git a/src/element/resizeTest.ts b/src/element/resizeTest.ts index 4471244dd3..3a794e2c59 100644 --- a/src/element/resizeTest.ts +++ b/src/element/resizeTest.ts @@ -173,57 +173,3 @@ export const getCursorForResizingElement = (resizingElement: { return cursor ? `${cursor}-resize` : ""; }; - -export const normalizeTransformHandleType = ( - element: ExcalidrawElement, - transformHandleType: TransformHandleType, -): TransformHandleType => { - if (element.width >= 0 && element.height >= 0) { - return transformHandleType; - } - - if (element.width < 0 && element.height < 0) { - switch (transformHandleType) { - case "nw": - return "se"; - case "ne": - return "sw"; - case "se": - return "nw"; - case "sw": - return "ne"; - } - } else if (element.width < 0) { - switch (transformHandleType) { - case "nw": - return "ne"; - case "ne": - return "nw"; - case "se": - return "sw"; - case "sw": - return "se"; - case "e": - return "w"; - case "w": - return "e"; - } - } else { - switch (transformHandleType) { - case "nw": - return "sw"; - case "ne": - return "se"; - case "se": - return "ne"; - case "sw": - return "nw"; - case "n": - return "s"; - case "s": - return "n"; - } - } - - return transformHandleType; -}; diff --git a/src/math.ts b/src/math.ts index dadc6ccca5..15a8aa6e3a 100644 --- a/src/math.ts +++ b/src/math.ts @@ -70,64 +70,6 @@ export const adjustXYWithRotation = ( return [x, y]; }; -export const getFlipAdjustment = ( - side: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se", - nextWidth: number, - nextHeight: number, - nextX1: number, - nextY1: number, - nextX2: number, - nextY2: number, - finalX1: number, - finalY1: number, - finalX2: number, - finalY2: number, - needsRotation: boolean, - angle: number, -): [number, number] => { - const cos = Math.cos(angle); - const sin = Math.sin(angle); - let flipDiffX = 0; - let flipDiffY = 0; - if (nextWidth < 0) { - if (side === "e" || side === "ne" || side === "se") { - if (needsRotation) { - flipDiffX += (finalX2 - nextX1) * cos; - flipDiffY += (finalX2 - nextX1) * sin; - } else { - flipDiffX += finalX2 - nextX1; - } - } - if (side === "w" || side === "nw" || side === "sw") { - if (needsRotation) { - flipDiffX += (finalX1 - nextX2) * cos; - flipDiffY += (finalX1 - nextX2) * sin; - } else { - flipDiffX += finalX1 - nextX2; - } - } - } - if (nextHeight < 0) { - if (side === "s" || side === "se" || side === "sw") { - if (needsRotation) { - flipDiffY += (finalY2 - nextY1) * cos; - flipDiffX += (finalY2 - nextY1) * -sin; - } else { - flipDiffY += finalY2 - nextY1; - } - } - if (side === "n" || side === "ne" || side === "nw") { - if (needsRotation) { - flipDiffY += (finalY1 - nextY2) * cos; - flipDiffX += (finalY1 - nextY2) * -sin; - } else { - flipDiffY += finalY1 - nextY2; - } - } - } - return [flipDiffX, flipDiffY]; -}; - export const getPointOnAPath = (point: Point, path: Point[]) => { const [px, py] = point; const [start, ...other] = path; diff --git a/src/tests/resize.test.tsx b/src/tests/resize.test.tsx index 8d207c08ef..70c79fe6b9 100644 --- a/src/tests/resize.test.tsx +++ b/src/tests/resize.test.tsx @@ -41,7 +41,7 @@ describe("resize rectangle ellipses and diamond elements", () => { ${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]} ${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]} ${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]} - ${"ne"} | ${[10, 55]} | ${[110, 45]} | ${[elemData.x, 55]} + ${"ne"} | ${[5, 55]} | ${[105, 45]} | ${[elemData.x, 55]} ${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]} ${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]} ${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]}