{"version":3,"file":"skew.mjs","sources":["../../../src/controls/skew.ts"],"sourcesContent":["import type {\n  ControlCursorCallback,\n  TPointerEvent,\n  Transform,\n  TransformActionHandler,\n} from '../EventTypeDefs';\nimport { resolveOrigin } from '../util/misc/resolveOrigin';\nimport { Point } from '../Point';\nimport type { TAxis, TAxisKey } from '../typedefs';\nimport {\n  degreesToRadians,\n  radiansToDegrees,\n} from '../util/misc/radiansDegreesConversion';\nimport {\n  findCornerQuadrant,\n  getLocalPoint,\n  isLocked,\n  NOT_ALLOWED_CURSOR,\n} from './util';\nimport { wrapWithFireEvent } from './wrapWithFireEvent';\nimport { wrapWithFixedAnchor } from './wrapWithFixedAnchor';\nimport {\n  CENTER,\n  SCALE_X,\n  SCALE_Y,\n  SKEWING,\n  SKEW_X,\n  SKEW_Y,\n} from '../constants';\n\nexport type SkewTransform = Transform & { skewingSide: -1 | 1 };\n\nconst AXIS_KEYS: Record<\n  TAxis,\n  {\n    counterAxis: TAxis;\n    scale: TAxisKey<'scale'>;\n    skew: TAxisKey<'skew'>;\n    lockSkewing: TAxisKey<'lockSkewing'>;\n    origin: TAxisKey<'origin'>;\n    flip: TAxisKey<'flip'>;\n  }\n> = {\n  x: {\n    counterAxis: 'y',\n    scale: SCALE_X,\n    skew: SKEW_X,\n    lockSkewing: 'lockSkewingX',\n    origin: 'originX',\n    flip: 'flipX',\n  },\n  y: {\n    counterAxis: 'x',\n    scale: SCALE_Y,\n    skew: SKEW_Y,\n    lockSkewing: 'lockSkewingY',\n    origin: 'originY',\n    flip: 'flipY',\n  },\n};\n\nconst skewMap = ['ns', 'nesw', 'ew', 'nwse'];\n\n/**\n * return the correct cursor style for the skew 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 skewCursorStyleHandler: ControlCursorCallback = (\n  eventData,\n  control,\n  fabricObject,\n) => {\n  if (control.x !== 0 && isLocked(fabricObject, 'lockSkewingY')) {\n    return NOT_ALLOWED_CURSOR;\n  }\n  if (control.y !== 0 && isLocked(fabricObject, 'lockSkewingX')) {\n    return NOT_ALLOWED_CURSOR;\n  }\n  const n = findCornerQuadrant(fabricObject, control) % 4;\n  return `${skewMap[n]}-resize`;\n};\n\n/**\n * Since skewing is applied before scaling, calculations are done in a scaleless plane\n * @see https://github.com/fabricjs/fabric.js/pull/8380\n */\nfunction skewObject(\n  axis: TAxis,\n  { target, ex, ey, skewingSide, ...transform }: SkewTransform,\n  pointer: Point,\n) {\n  const { skew: skewKey } = AXIS_KEYS[axis],\n    offset = pointer\n      .subtract(new Point(ex, ey))\n      .divide(new Point(target.scaleX, target.scaleY))[axis],\n    skewingBefore = target[skewKey],\n    skewingStart = transform[skewKey],\n    shearingStart = Math.tan(degreesToRadians(skewingStart)),\n    // let a, b be the size of target\n    // let a' be the value of a after applying skewing\n    // then:\n    // a' = a + b * skewA => skewA = (a' - a) / b\n    // the value b is tricky since skewY is applied before skewX\n    b =\n      axis === 'y'\n        ? target._getTransformedDimensions({\n            scaleX: 1,\n            scaleY: 1,\n            // since skewY is applied before skewX, b (=width) is not affected by skewX\n            skewX: 0,\n          }).x\n        : target._getTransformedDimensions({\n            scaleX: 1,\n            scaleY: 1,\n          }).y;\n\n  const shearing =\n    (2 * offset * skewingSide) /\n      // we max out fractions to safeguard from asymptotic behavior\n      Math.max(b, 1) +\n    // add starting state\n    shearingStart;\n\n  const skewing = radiansToDegrees(Math.atan(shearing));\n\n  target.set(skewKey, skewing);\n  const changed = skewingBefore !== target[skewKey];\n\n  if (changed && axis === 'y') {\n    // we don't want skewing to affect scaleX\n    // so we factor it by the inverse skewing diff to make it seem unchanged to the viewer\n    const { skewX, scaleX } = target,\n      dimBefore = target._getTransformedDimensions({ skewY: skewingBefore }),\n      dimAfter = target._getTransformedDimensions(),\n      compensationFactor = skewX !== 0 ? dimBefore.x / dimAfter.x : 1;\n    compensationFactor !== 1 &&\n      target.set(SCALE_X, compensationFactor * scaleX);\n  }\n\n  return changed;\n}\n\n/**\n * Wrapped Action handler for skewing on a given axis, takes care of the\n * skew direction and determines the correct transform origin for the anchor point\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 */\nfunction skewHandler(\n  axis: TAxis,\n  eventData: TPointerEvent,\n  transform: Transform,\n  x: number,\n  y: number,\n) {\n  const { target } = transform,\n    {\n      counterAxis,\n      origin: originKey,\n      lockSkewing: lockSkewingKey,\n      skew: skewKey,\n      flip: flipKey,\n    } = AXIS_KEYS[axis];\n  if (isLocked(target, lockSkewingKey)) {\n    return false;\n  }\n\n  const { origin: counterOriginKey, flip: counterFlipKey } =\n      AXIS_KEYS[counterAxis],\n    counterOriginFactor =\n      resolveOrigin(transform[counterOriginKey]) *\n      (target[counterFlipKey] ? -1 : 1),\n    // if the counter origin is top/left (= -0.5) then we are skewing x/y values on the bottom/right side of target respectively.\n    // if the counter origin is bottom/right (= 0.5) then we are skewing x/y values on the top/left side of target respectively.\n    // skewing direction on the top/left side of target is OPPOSITE to the direction of the movement of the pointer,\n    // so we factor skewing direction by this value.\n    skewingSide = (-Math.sign(counterOriginFactor) *\n      (target[flipKey] ? -1 : 1)) as 1 | -1,\n    skewingDirection =\n      ((target[skewKey] === 0 &&\n        // in case skewing equals 0 we use the pointer offset from target center to determine the direction of skewing\n        getLocalPoint(transform, CENTER, CENTER, x, y)[axis] > 0) ||\n      // in case target has skewing we use that as the direction\n      target[skewKey] > 0\n        ? 1\n        : -1) * skewingSide,\n    // anchor to the opposite side of the skewing direction\n    // normalize value from [-1, 1] to origin value [0, 1]\n    origin = -skewingDirection * 0.5 + 0.5;\n\n  const finalHandler = wrapWithFireEvent<SkewTransform>(\n    SKEWING,\n    wrapWithFixedAnchor((eventData, transform, x, y) =>\n      skewObject(axis, transform, new Point(x, y)),\n    ),\n  );\n\n  return finalHandler(\n    eventData,\n    {\n      ...transform,\n      [originKey]: origin,\n      skewingSide,\n    },\n    x,\n    y,\n  );\n}\n\n/**\n * Wrapped Action handler for skewing on the X axis, takes care of the\n * skew direction and determines the correct transform origin for the anchor point\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 skewHandlerX: TransformActionHandler = (\n  eventData,\n  transform,\n  x,\n  y,\n) => {\n  return skewHandler('x', eventData, transform, x, y);\n};\n\n/**\n * Wrapped Action handler for skewing on the Y axis, takes care of the\n * skew direction and determines the correct transform origin for the anchor point\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 skewHandlerY: TransformActionHandler = (\n  eventData,\n  transform,\n  x,\n  y,\n) => {\n  return skewHandler('y', eventData, transform, x, y);\n};\n"],"names":["AXIS_KEYS","x","counterAxis","scale","SCALE_X","skew","SKEW_X","lockSkewing","origin","flip","y","SCALE_Y","SKEW_Y","skewMap","skewCursorStyleHandler","eventData","control","fabricObject","isLocked","NOT_ALLOWED_CURSOR","n","findCornerQuadrant","concat","skewObject","axis","_ref","pointer","target","ex","ey","skewingSide","transform","_objectWithoutProperties","_excluded","skewKey","offset","subtract","Point","divide","scaleX","scaleY","skewingBefore","skewingStart","shearingStart","Math","tan","degreesToRadians","b","_getTransformedDimensions","skewX","shearing","max","skewing","radiansToDegrees","atan","set","changed","dimBefore","skewY","dimAfter","compensationFactor","skewHandler","originKey","lockSkewingKey","flipKey","counterOriginKey","counterFlipKey","counterOriginFactor","resolveOrigin","sign","skewingDirection","getLocalPoint","CENTER","finalHandler","wrapWithFireEvent","SKEWING","wrapWithFixedAnchor","_objectSpread","skewHandlerX","skewHandlerY"],"mappings":"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"}