mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-10-26 08:24:20 +01:00 
			
		
		
		
	 47f87f4ecb
			
		
	
	47f87f4ecb
	
	
	
		
			
			* fix: remove scene from getElementAbsoluteCoords and dependent functions and use elementsMap * lint * fix * use non deleted elements where possible * use non deleted elements map in actions * pass elementsMap instead of array to elementOverlapsWithFrame * lint * fix * pass elementsMap to getElementsCorners * pass elementsMap to getEligibleElementsForBinding * pass elementsMap in bindOrUnbindSelectedElements and unbindLinearElements * pass elementsMap in elementsAreInFrameBounds,elementOverlapsWithFrame,isCursorInFrame,getElementsInResizingFrame * pass elementsMap in getElementsWithinSelection, getElementsCompletelyInFrame, isElementContainingFrame, getElementsInNewFrame * pass elementsMap to getElementWithTransformHandleType * pass elementsMap to getVisibleGaps, getMaximumGroups,getReferenceSnapPoints,snapDraggedElements * lint * pass elementsMap to bindTextToShapeAfterDuplication,bindLinearElementToElement,getTextBindableContainerAtPosition * revert changes for bindTextToShapeAfterDuplication
		
			
				
	
	
		
			299 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   ElementsMap,
 | |
|   ExcalidrawElement,
 | |
|   NonDeletedExcalidrawElement,
 | |
|   PointerType,
 | |
| } from "./types";
 | |
| 
 | |
| import { Bounds, getElementAbsoluteCoords } from "./bounds";
 | |
| import { rotate } from "../math";
 | |
| import { InteractiveCanvasAppState, Zoom } from "../types";
 | |
| import { isTextElement } from ".";
 | |
| import { isFrameLikeElement, isLinearElement } from "./typeChecks";
 | |
| import { DEFAULT_TRANSFORM_HANDLE_SPACING } from "../constants";
 | |
| 
 | |
| export type TransformHandleDirection =
 | |
|   | "n"
 | |
|   | "s"
 | |
|   | "w"
 | |
|   | "e"
 | |
|   | "nw"
 | |
|   | "ne"
 | |
|   | "sw"
 | |
|   | "se";
 | |
| 
 | |
| export type TransformHandleType = TransformHandleDirection | "rotation";
 | |
| 
 | |
| export type TransformHandle = Bounds;
 | |
| export type TransformHandles = Partial<{
 | |
|   [T in TransformHandleType]: TransformHandle;
 | |
| }>;
 | |
| export type MaybeTransformHandleType = TransformHandleType | false;
 | |
| 
 | |
| const transformHandleSizes: { [k in PointerType]: number } = {
 | |
|   mouse: 8,
 | |
|   pen: 16,
 | |
|   touch: 28,
 | |
| };
 | |
| 
 | |
| const ROTATION_RESIZE_HANDLE_GAP = 16;
 | |
| 
 | |
| export const OMIT_SIDES_FOR_MULTIPLE_ELEMENTS = {
 | |
|   e: true,
 | |
|   s: true,
 | |
|   n: true,
 | |
|   w: true,
 | |
| };
 | |
| 
 | |
| export const OMIT_SIDES_FOR_FRAME = {
 | |
|   e: true,
 | |
|   s: true,
 | |
|   n: true,
 | |
|   w: true,
 | |
|   rotation: true,
 | |
| };
 | |
| 
 | |
| const OMIT_SIDES_FOR_TEXT_ELEMENT = {
 | |
|   e: true,
 | |
|   s: true,
 | |
|   n: true,
 | |
|   w: true,
 | |
| };
 | |
| 
 | |
| const OMIT_SIDES_FOR_LINE_SLASH = {
 | |
|   e: true,
 | |
|   s: true,
 | |
|   n: true,
 | |
|   w: true,
 | |
|   nw: true,
 | |
|   se: true,
 | |
| };
 | |
| 
 | |
| const OMIT_SIDES_FOR_LINE_BACKSLASH = {
 | |
|   e: true,
 | |
|   s: true,
 | |
|   n: true,
 | |
|   w: true,
 | |
| };
 | |
| 
 | |
| const generateTransformHandle = (
 | |
|   x: number,
 | |
|   y: number,
 | |
|   width: number,
 | |
|   height: number,
 | |
|   cx: number,
 | |
|   cy: number,
 | |
|   angle: number,
 | |
| ): TransformHandle => {
 | |
|   const [xx, yy] = rotate(x + width / 2, y + height / 2, cx, cy, angle);
 | |
|   return [xx - width / 2, yy - height / 2, width, height];
 | |
| };
 | |
| 
 | |
| export const getTransformHandlesFromCoords = (
 | |
|   [x1, y1, x2, y2, cx, cy]: [number, number, number, number, number, number],
 | |
|   angle: number,
 | |
|   zoom: Zoom,
 | |
|   pointerType: PointerType,
 | |
|   omitSides: { [T in TransformHandleType]?: boolean } = {},
 | |
|   margin = 4,
 | |
| ): TransformHandles => {
 | |
|   const size = transformHandleSizes[pointerType];
 | |
|   const handleWidth = size / zoom.value;
 | |
|   const handleHeight = size / zoom.value;
 | |
| 
 | |
|   const handleMarginX = size / zoom.value;
 | |
|   const handleMarginY = size / zoom.value;
 | |
| 
 | |
|   const width = x2 - x1;
 | |
|   const height = y2 - y1;
 | |
|   const dashedLineMargin = margin / zoom.value;
 | |
|   const centeringOffset =
 | |
|     (size - DEFAULT_TRANSFORM_HANDLE_SPACING * 2) / (2 * zoom.value);
 | |
| 
 | |
|   const transformHandles: TransformHandles = {
 | |
|     nw: omitSides.nw
 | |
|       ? undefined
 | |
|       : generateTransformHandle(
 | |
|           x1 - dashedLineMargin - handleMarginX + centeringOffset,
 | |
|           y1 - dashedLineMargin - handleMarginY + centeringOffset,
 | |
|           handleWidth,
 | |
|           handleHeight,
 | |
|           cx,
 | |
|           cy,
 | |
|           angle,
 | |
|         ),
 | |
|     ne: omitSides.ne
 | |
|       ? undefined
 | |
|       : generateTransformHandle(
 | |
|           x2 + dashedLineMargin - centeringOffset,
 | |
|           y1 - dashedLineMargin - handleMarginY + centeringOffset,
 | |
|           handleWidth,
 | |
|           handleHeight,
 | |
|           cx,
 | |
|           cy,
 | |
|           angle,
 | |
|         ),
 | |
|     sw: omitSides.sw
 | |
|       ? undefined
 | |
|       : generateTransformHandle(
 | |
|           x1 - dashedLineMargin - handleMarginX + centeringOffset,
 | |
|           y2 + dashedLineMargin - centeringOffset,
 | |
|           handleWidth,
 | |
|           handleHeight,
 | |
|           cx,
 | |
|           cy,
 | |
|           angle,
 | |
|         ),
 | |
|     se: omitSides.se
 | |
|       ? undefined
 | |
|       : generateTransformHandle(
 | |
|           x2 + dashedLineMargin - centeringOffset,
 | |
|           y2 + dashedLineMargin - centeringOffset,
 | |
|           handleWidth,
 | |
|           handleHeight,
 | |
|           cx,
 | |
|           cy,
 | |
|           angle,
 | |
|         ),
 | |
|     rotation: omitSides.rotation
 | |
|       ? undefined
 | |
|       : generateTransformHandle(
 | |
|           x1 + width / 2 - handleWidth / 2,
 | |
|           y1 -
 | |
|             dashedLineMargin -
 | |
|             handleMarginY +
 | |
|             centeringOffset -
 | |
|             ROTATION_RESIZE_HANDLE_GAP / zoom.value,
 | |
|           handleWidth,
 | |
|           handleHeight,
 | |
|           cx,
 | |
|           cy,
 | |
|           angle,
 | |
|         ),
 | |
|   };
 | |
| 
 | |
|   // We only want to show height handles (all cardinal directions)  above a certain size
 | |
|   // Note: we render using "mouse" size so we should also use "mouse" size for this check
 | |
|   const minimumSizeForEightHandles =
 | |
|     (5 * transformHandleSizes.mouse) / zoom.value;
 | |
|   if (Math.abs(width) > minimumSizeForEightHandles) {
 | |
|     if (!omitSides.n) {
 | |
|       transformHandles.n = generateTransformHandle(
 | |
|         x1 + width / 2 - handleWidth / 2,
 | |
|         y1 - dashedLineMargin - handleMarginY + centeringOffset,
 | |
|         handleWidth,
 | |
|         handleHeight,
 | |
|         cx,
 | |
|         cy,
 | |
|         angle,
 | |
|       );
 | |
|     }
 | |
|     if (!omitSides.s) {
 | |
|       transformHandles.s = generateTransformHandle(
 | |
|         x1 + width / 2 - handleWidth / 2,
 | |
|         y2 + dashedLineMargin - centeringOffset,
 | |
|         handleWidth,
 | |
|         handleHeight,
 | |
|         cx,
 | |
|         cy,
 | |
|         angle,
 | |
|       );
 | |
|     }
 | |
|   }
 | |
|   if (Math.abs(height) > minimumSizeForEightHandles) {
 | |
|     if (!omitSides.w) {
 | |
|       transformHandles.w = generateTransformHandle(
 | |
|         x1 - dashedLineMargin - handleMarginX + centeringOffset,
 | |
|         y1 + height / 2 - handleHeight / 2,
 | |
|         handleWidth,
 | |
|         handleHeight,
 | |
|         cx,
 | |
|         cy,
 | |
|         angle,
 | |
|       );
 | |
|     }
 | |
|     if (!omitSides.e) {
 | |
|       transformHandles.e = generateTransformHandle(
 | |
|         x2 + dashedLineMargin - centeringOffset,
 | |
|         y1 + height / 2 - handleHeight / 2,
 | |
|         handleWidth,
 | |
|         handleHeight,
 | |
|         cx,
 | |
|         cy,
 | |
|         angle,
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return transformHandles;
 | |
| };
 | |
| 
 | |
| export const getTransformHandles = (
 | |
|   element: ExcalidrawElement,
 | |
|   zoom: Zoom,
 | |
|   elementsMap: ElementsMap,
 | |
| 
 | |
|   pointerType: PointerType = "mouse",
 | |
| ): TransformHandles => {
 | |
|   // so that when locked element is selected (especially when you toggle lock
 | |
|   // via keyboard) the locked element is visually distinct, indicating
 | |
|   // you can't move/resize
 | |
|   if (element.locked) {
 | |
|     return {};
 | |
|   }
 | |
| 
 | |
|   let omitSides: { [T in TransformHandleType]?: boolean } = {};
 | |
|   if (element.type === "freedraw" || isLinearElement(element)) {
 | |
|     if (element.points.length === 2) {
 | |
|       // only check the last point because starting point is always (0,0)
 | |
|       const [, p1] = element.points;
 | |
|       if (p1[0] === 0 || p1[1] === 0) {
 | |
|         omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
 | |
|       } else if (p1[0] > 0 && p1[1] < 0) {
 | |
|         omitSides = OMIT_SIDES_FOR_LINE_SLASH;
 | |
|       } else if (p1[0] > 0 && p1[1] > 0) {
 | |
|         omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
 | |
|       } else if (p1[0] < 0 && p1[1] > 0) {
 | |
|         omitSides = OMIT_SIDES_FOR_LINE_SLASH;
 | |
|       } else if (p1[0] < 0 && p1[1] < 0) {
 | |
|         omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
 | |
|       }
 | |
|     }
 | |
|   } else if (isTextElement(element)) {
 | |
|     omitSides = OMIT_SIDES_FOR_TEXT_ELEMENT;
 | |
|   } else if (isFrameLikeElement(element)) {
 | |
|     omitSides = {
 | |
|       rotation: true,
 | |
|     };
 | |
|   }
 | |
|   const dashedLineMargin = isLinearElement(element)
 | |
|     ? DEFAULT_TRANSFORM_HANDLE_SPACING + 8
 | |
|     : DEFAULT_TRANSFORM_HANDLE_SPACING;
 | |
|   return getTransformHandlesFromCoords(
 | |
|     getElementAbsoluteCoords(element, elementsMap, true),
 | |
|     element.angle,
 | |
|     zoom,
 | |
|     pointerType,
 | |
|     omitSides,
 | |
|     dashedLineMargin,
 | |
|   );
 | |
| };
 | |
| 
 | |
| export const shouldShowBoundingBox = (
 | |
|   elements: readonly NonDeletedExcalidrawElement[],
 | |
|   appState: InteractiveCanvasAppState,
 | |
| ) => {
 | |
|   if (appState.editingLinearElement) {
 | |
|     return false;
 | |
|   }
 | |
|   if (elements.length > 1) {
 | |
|     return true;
 | |
|   }
 | |
|   const element = elements[0];
 | |
|   if (!isLinearElement(element)) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return element.points.length > 2;
 | |
| };
 |