{"version":3,"file":"scale.min.mjs","sources":["../../../src/controls/scale.ts"],"sourcesContent":["import type {\n  ControlCursorCallback,\n  TPointerEvent,\n  Transform,\n  TransformActionHandler,\n} from '../EventTypeDefs';\nimport type { FabricObject } from '../shapes/Object/FabricObject';\nimport type { TAxis } from '../typedefs';\nimport type { Canvas } from '../canvas/Canvas';\nimport {\n  findCornerQuadrant,\n  getLocalPoint,\n  invertOrigin,\n  isLocked,\n  isTransformCentered,\n  NOT_ALLOWED_CURSOR,\n} from './util';\nimport { wrapWithFireEvent } from './wrapWithFireEvent';\nimport { wrapWithFixedAnchor } from './wrapWithFixedAnchor';\nimport { SCALE_X, SCALE_Y, SCALING } from '../constants';\n\ntype ScaleTransform = Transform & {\n  gestureScale?: number;\n  signX?: number;\n  signY?: number;\n};\n\ntype ScaleBy = TAxis | 'equally' | '' | undefined;\n\n/**\n * Inspect event and fabricObject properties to understand if the scaling action\n * @param {Event} eventData from the user action\n * @param {FabricObject} fabricObject the fabric object about to scale\n * @return {Boolean} true if scale is proportional\n */\nexport function scaleIsProportional(\n  eventData: TPointerEvent,\n  fabricObject: FabricObject\n): boolean {\n  const canvas = fabricObject.canvas as Canvas,\n    uniformIsToggled = eventData[canvas.uniScaleKey!];\n  return (\n    (canvas.uniformScaling && !uniformIsToggled) ||\n    (!canvas.uniformScaling && uniformIsToggled)\n  );\n}\n\n/**\n * Inspect fabricObject to understand if the current scaling action is allowed\n * @param {FabricObject} fabricObject the fabric object about to scale\n * @param {String} by 'x' or 'y' or ''\n * @param {Boolean} scaleProportionally true if we are trying to scale proportionally\n * @return {Boolean} true if scaling is not allowed at current conditions\n */\nexport function scalingIsForbidden(\n  fabricObject: FabricObject,\n  by: ScaleBy,\n  scaleProportionally: boolean\n) {\n  const lockX = isLocked(fabricObject, 'lockScalingX'),\n    lockY = isLocked(fabricObject, 'lockScalingY');\n  if (lockX && lockY) {\n    return true;\n  }\n  if (!by && (lockX || lockY) && scaleProportionally) {\n    return true;\n  }\n  if (lockX && by === 'x') {\n    return true;\n  }\n  if (lockY && by === 'y') {\n    return true;\n  }\n  // code crashes because of a division by 0 if a 0 sized object is scaled\n  // forbid to prevent scaling to happen. ISSUE-9475\n  const { width, height, strokeWidth } = fabricObject;\n  if (width === 0 && strokeWidth === 0 && by !== 'y') {\n    return true;\n  }\n  if (height === 0 && strokeWidth === 0 && by !== 'x') {\n    return true;\n  }\n  return false;\n}\n\nconst scaleMap = ['e', 'se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e'];\n\n/**\n * return the correct cursor style for the scale action\n * @param {Event} eventData the javascript event that is causing the scale\n * @param {Control} control the control that is interested in the action\n * @param {FabricObject} fabricObject the fabric object that is interested in the action\n * @return {String} a valid css string for the cursor\n */\nexport const scaleCursorStyleHandler: ControlCursorCallback = (\n  eventData,\n  control,\n  fabricObject\n) => {\n  const scaleProportionally = scaleIsProportional(eventData, fabricObject),\n    by =\n      control.x !== 0 && control.y === 0\n        ? 'x'\n        : control.x === 0 && control.y !== 0\n        ? 'y'\n        : '';\n  if (scalingIsForbidden(fabricObject, by, scaleProportionally)) {\n    return NOT_ALLOWED_CURSOR;\n  }\n  const n = findCornerQuadrant(fabricObject, control);\n  return `${scaleMap[n]}-resize`;\n};\n\n/**\n * Basic scaling logic, reused with different constrain for scaling X,Y, freely or equally.\n * Needs to be wrapped with `wrapWithFixedAnchor` to be effective\n * @param {Event} eventData javascript event that is doing the transform\n * @param {Object} transform javascript object containing a series of information around the current transform\n * @param {number} x current mouse x position, canvas normalized\n * @param {number} y current mouse y position, canvas normalized\n * @param {Object} options additional information for scaling\n * @param {String} options.by 'x', 'y', 'equally' or '' to indicate type of scaling\n * @return {Boolean} true if some change happened\n * @private\n */\nfunction scaleObject(\n  eventData: TPointerEvent,\n  transform: ScaleTransform,\n  x: number,\n  y: number,\n  options: { by?: ScaleBy } = {}\n) {\n  const target = transform.target,\n    by = options.by,\n    scaleProportionally = scaleIsProportional(eventData, target),\n    forbidScaling = scalingIsForbidden(target, by, scaleProportionally);\n  let newPoint, scaleX, scaleY, dim, signX, signY;\n\n  if (forbidScaling) {\n    return false;\n  }\n  if (transform.gestureScale) {\n    scaleX = transform.scaleX * transform.gestureScale;\n    scaleY = transform.scaleY * transform.gestureScale;\n  } else {\n    newPoint = getLocalPoint(\n      transform,\n      transform.originX,\n      transform.originY,\n      x,\n      y\n    );\n    // use of sign: We use sign to detect change of direction of an action. sign usually change when\n    // we cross the origin point with the mouse. So a scale flip for example. There is an issue when scaling\n    // by center and scaling using one middle control ( default: mr, mt, ml, mb), the mouse movement can easily\n    // cross many time the origin point and flip the object. so we need a way to filter out the noise.\n    // This ternary here should be ok to filter out X scaling when we want Y only and vice versa.\n    signX = by !== 'y' ? Math.sign(newPoint.x || transform.signX || 1) : 1;\n    signY = by !== 'x' ? Math.sign(newPoint.y || transform.signY || 1) : 1;\n    if (!transform.signX) {\n      transform.signX = signX;\n    }\n    if (!transform.signY) {\n      transform.signY = signY;\n    }\n\n    if (\n      isLocked(target, 'lockScalingFlip') &&\n      (transform.signX !== signX || transform.signY !== signY)\n    ) {\n      return false;\n    }\n\n    dim = target._getTransformedDimensions();\n    // missing detection of flip and logic to switch the origin\n    if (scaleProportionally && !by) {\n      // uniform scaling\n      const distance = Math.abs(newPoint.x) + Math.abs(newPoint.y),\n        { original } = transform,\n        originalDistance =\n          Math.abs((dim.x * original.scaleX) / target.scaleX) +\n          Math.abs((dim.y * original.scaleY) / target.scaleY),\n        scale = distance / originalDistance;\n      scaleX = original.scaleX * scale;\n      scaleY = original.scaleY * scale;\n    } else {\n      scaleX = Math.abs((newPoint.x * target.scaleX) / dim.x);\n      scaleY = Math.abs((newPoint.y * target.scaleY) / dim.y);\n    }\n    // if we are scaling by center, we need to double the scale\n    if (isTransformCentered(transform)) {\n      scaleX *= 2;\n      scaleY *= 2;\n    }\n    if (transform.signX !== signX && by !== 'y') {\n      transform.originX = invertOrigin(transform.originX);\n      scaleX *= -1;\n      transform.signX = signX;\n    }\n    if (transform.signY !== signY && by !== 'x') {\n      transform.originY = invertOrigin(transform.originY);\n      scaleY *= -1;\n      transform.signY = signY;\n    }\n  }\n  // minScale is taken care of in the setter.\n  const oldScaleX = target.scaleX,\n    oldScaleY = target.scaleY;\n  if (!by) {\n    !isLocked(target, 'lockScalingX') && target.set(SCALE_X, scaleX);\n    !isLocked(target, 'lockScalingY') && target.set(SCALE_Y, scaleY);\n  } else {\n    // forbidden cases already handled on top here.\n    by === 'x' && target.set(SCALE_X, scaleX);\n    by === 'y' && target.set(SCALE_Y, scaleY);\n  }\n  return oldScaleX !== target.scaleX || oldScaleY !== target.scaleY;\n}\n\n/**\n * Generic scaling logic, to scale from corners either equally or freely.\n * Needs to be wrapped with `wrapWithFixedAnchor` to be effective\n * @param {Event} eventData javascript event that is doing the transform\n * @param {Object} transform javascript object containing a series of information around the current transform\n * @param {number} x current mouse x position, canvas normalized\n * @param {number} y current mouse y position, canvas normalized\n * @return {Boolean} true if some change happened\n */\nexport const scaleObjectFromCorner: TransformActionHandler<ScaleTransform> = (\n  eventData,\n  transform,\n  x,\n  y\n) => {\n  return scaleObject(eventData, transform, x, y);\n};\n\n/**\n * Scaling logic for the X axis.\n * Needs to be wrapped with `wrapWithFixedAnchor` to be effective\n * @param {Event} eventData javascript event that is doing the transform\n * @param {Object} transform javascript object containing a series of information around the current transform\n * @param {number} x current mouse x position, canvas normalized\n * @param {number} y current mouse y position, canvas normalized\n * @return {Boolean} true if some change happened\n */\nconst scaleObjectX: TransformActionHandler<ScaleTransform> = (\n  eventData,\n  transform,\n  x,\n  y\n) => {\n  return scaleObject(eventData, transform, x, y, { by: 'x' });\n};\n\n/**\n * Scaling logic for the Y axis.\n * Needs to be wrapped with `wrapWithFixedAnchor` to be effective\n * @param {Event} eventData javascript event that is doing the transform\n * @param {Object} transform javascript object containing a series of information around the current transform\n * @param {number} x current mouse x position, canvas normalized\n * @param {number} y current mouse y position, canvas normalized\n * @return {Boolean} true if some change happened\n */\nconst scaleObjectY: TransformActionHandler<ScaleTransform> = (\n  eventData,\n  transform,\n  x,\n  y\n) => {\n  return scaleObject(eventData, transform, x, y, { by: 'y' });\n};\n\nexport const scalingEqually = wrapWithFireEvent(\n  SCALING,\n  wrapWithFixedAnchor(scaleObjectFromCorner)\n);\n\nexport const scalingX = wrapWithFireEvent(\n  SCALING,\n  wrapWithFixedAnchor(scaleObjectX)\n);\n\nexport const scalingY = wrapWithFireEvent(\n  SCALING,\n  wrapWithFixedAnchor(scaleObjectY)\n);\n"],"names":["scaleIsProportional","eventData","fabricObject","canvas","uniformIsToggled","uniScaleKey","uniformScaling","scalingIsForbidden","by","scaleProportionally","lockX","isLocked","lockY","width","height","strokeWidth","scaleMap","scaleCursorStyleHandler","control","x","y","NOT_ALLOWED_CURSOR","n","findCornerQuadrant","concat","scaleObject","transform","options","arguments","length","undefined","target","newPoint","scaleX","scaleY","dim","signX","signY","gestureScale","getLocalPoint","originX","originY","Math","sign","_getTransformedDimensions","distance","abs","original","scale","isTransformCentered","invertOrigin","oldScaleX","oldScaleY","set","SCALE_X","SCALE_Y","scaleObjectFromCorner","scalingEqually","wrapWithFireEvent","SCALING","wrapWithFixedAnchor","scalingX","scaleObjectX","scalingY","scaleObjectY"],"mappings":"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"}