diff --git a/src/components/App.tsx b/src/components/App.tsx index cd4f7ed2..bf578f12 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -87,9 +87,9 @@ import { getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, - getPerfectElementSize, getResizeArrowDirection, getResizeOffsetXY, + getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, hitTest, isHittingElementBoundingBoxWithoutHittingElement, @@ -2768,10 +2768,13 @@ class App extends React.Component { if (shouldRotateWithDiscreteAngle(event)) { ({ width: dxFromLastCommitted, height: dyFromLastCommitted } = - getPerfectElementSize( - this.state.activeTool.type, - dxFromLastCommitted, - dyFromLastCommitted, + getLockedLinearCursorAlignSize( + // actual coordinate of the last committed point + lastCommittedX + rx, + lastCommittedY + ry, + // cursor-grid coordinate + gridX, + gridY, )); } @@ -4241,10 +4244,11 @@ class App extends React.Component { let dy = gridY - draggingElement.y; if (shouldRotateWithDiscreteAngle(event) && points.length === 2) { - ({ width: dx, height: dy } = getPerfectElementSize( - this.state.activeTool.type, - dx, - dy, + ({ width: dx, height: dy } = getLockedLinearCursorAlignSize( + draggingElement.x, + draggingElement.y, + pointerCoords.x, + pointerCoords.y, )); } diff --git a/src/element/index.ts b/src/element/index.ts index ef4059c9..ab9b0edc 100644 --- a/src/element/index.ts +++ b/src/element/index.ts @@ -53,6 +53,7 @@ export { textWysiwyg } from "./textWysiwyg"; export { redrawTextBoundingBox } from "./textElement"; export { getPerfectElementSize, + getLockedLinearCursorAlignSize, isInvisiblySmallElement, resizePerfectLineForNWHandler, getNormalizedDimensions, diff --git a/src/element/sizeHelpers.ts b/src/element/sizeHelpers.ts index 454cb80f..c87f607f 100644 --- a/src/element/sizeHelpers.ts +++ b/src/element/sizeHelpers.ts @@ -47,6 +47,46 @@ export const getPerfectElementSize = ( return { width, height }; }; +export const getLockedLinearCursorAlignSize = ( + originX: number, + originY: number, + x: number, + y: number, +) => { + let width = x - originX; + let height = y - originY; + + const lockedAngle = + Math.round(Math.atan(height / width) / SHIFT_LOCKING_ANGLE) * + SHIFT_LOCKING_ANGLE; + + if (lockedAngle === 0) { + height = 0; + } else if (lockedAngle === Math.PI / 2) { + width = 0; + } else { + // locked angle line, y = mx + b => mx - y + b = 0 + const a1 = Math.tan(lockedAngle); + const b1 = -1; + const c1 = originY - a1 * originX; + + // line through cursor, perpendicular to locked angle line + const a2 = -1 / a1; + const b2 = -1; + const c2 = y - a2 * x; + + // intersection of the two lines above + const intersectX = Math.round((b1 * c2 - b2 * c1) / (a1 * b2 - a2 * b1)); + const intersectY = Math.round((c1 * a2 - c2 * a1) / (a1 * b2 - a2 * b1)); + + // delta + width = intersectX - originX; + height = intersectY - originY; + } + + return { width, height }; +}; + export const resizePerfectLineForNWHandler = ( element: ExcalidrawElement, x: number,