# @interactjs/modifiers/aspectRatio.ts

    /* eslint-disable */

    /**
     * @module modifiers/aspectRatio
     *
     * @description
     * This module forces elements to be resized with a specified dx/dy ratio.
     *
     * @example
     * interact(target).resizable({
     *   modifiers: [
     *     interact.modifiers.snapSize({
     *       targets: [ interact.createSnapGrid({ x: 20, y: 20 }) ],
     *     }),
     *     interact.aspectRatio({ ratio: 'preserve' }),
     *   ],
     * });
     */

    import extend from '@interactjs/utils/extend'
    import { addEdges } from '@interactjs/utils/rect'
    import { Modifier, ModifierModule, ModifierState } from './base'
    import Modification from './Modification'
    import { makeModifier } from './base'

    export interface AspectRatioOptions {
      ratio?: number | 'preserve'
      equalDelta?: boolean
      modifiers?: Modifier[]
      enabled?: boolean
    }

    export type AspectRatioState = ModifierState<AspectRatioOptions, {
      startCoords: Interact.Point
      startRect: Interact.Rect
      linkedEdges: Interact.EdgeOptions
      ratio: number
      equalDelta: boolean
      xIsPrimaryAxis: boolean
      edgeSign: 1 | -1
      subModification: Modification
    }>

    const aspectRatio: ModifierModule<AspectRatioOptions, AspectRatioState> = {
      start (arg) {
        const { state, rect, edges: originalEdges, pageCoords: coords } = arg
        let { ratio } = state.options
        const { equalDelta, modifiers } = state.options

        if (ratio === 'preserve') {
          ratio = rect.width / rect.height
        }

        state.startCoords = extend({}, coords)
        state.startRect = extend({}, rect)
        state.ratio = ratio
        state.equalDelta = equalDelta

        const linkedEdges = state.linkedEdges = {
          top   : originalEdges.top    || (originalEdges.left   && !originalEdges.bottom),
          left  : originalEdges.left   || (originalEdges.top    && !originalEdges.right),
          bottom: originalEdges.bottom || (originalEdges.right  && !originalEdges.top),
          right : originalEdges.right  || (originalEdges.bottom && !originalEdges.left),
        }

        state.xIsPrimaryAxis = !!(originalEdges.left || originalEdges.right)

        if (state.equalDelta) {
          state.edgeSign = (linkedEdges.left ? 1 : -1) * (linkedEdges.top ? 1 : -1) as 1 | -1
        }
        else {
          const negativeSecondaryEdge = state.xIsPrimaryAxis ? linkedEdges.top : linkedEdges.left
          state.edgeSign = negativeSecondaryEdge ? -1 : 1
        }

        extend(arg.edges, linkedEdges)

        if (!modifiers || !modifiers.length) { return }

        const subModification = new Modification(arg.interaction)

        subModification.copyFrom(arg.interaction.modification)
        subModification.prepareStates(modifiers)

        state.subModification = subModification
        subModification.startAll({ ...arg })
      },

      set (arg) {
        const { state, rect, coords } = arg
        const initialCoords = extend({}, coords)
        const aspectMethod = state.equalDelta ? setEqualDelta : setRatio

        aspectMethod(state, state.xIsPrimaryAxis, coords, rect)

        if (!state.subModification) { return null }

        const correctedRect = extend({}, rect)

        addEdges(state.linkedEdges, correctedRect, { x: coords.x - initialCoords.x, y: coords.y - initialCoords.y })

        const result = state.subModification.setAll({
          ...arg,
          rect: correctedRect,
          edges: state.linkedEdges,
          pageCoords: coords,
          prevCoords: coords,
          prevRect: correctedRect,
        })

        const { delta } = result

        if (result.changed) {
          const xIsCriticalAxis = Math.abs(delta.x) > Math.abs(delta.y)

          // do aspect modification again with critical edge axis as primary
          aspectMethod(state, xIsCriticalAxis, result.coords, result.rect)
          extend(coords, result.coords)
        }

        return result.eventProps
      },

      defaults: {
        ratio: 'preserve',
        equalDelta: false,
        modifiers: [],
        enabled: false,
      },
    }

    function setEqualDelta ({ startCoords, edgeSign }: AspectRatioState, xIsPrimaryAxis: boolean, coords: Interact.Point) {
      if (xIsPrimaryAxis) {
        coords.y = startCoords.y + (coords.x - startCoords.x) * edgeSign
      }
      else {
        coords.x = startCoords.x + (coords.y - startCoords.y) * edgeSign
      }
    }

    function setRatio ({ startRect, startCoords, ratio, edgeSign }: AspectRatioState, xIsPrimaryAxis: boolean, coords: Interact.Point, rect: Interact.Rect) {
      if (xIsPrimaryAxis) {
        const newHeight = rect.width / ratio

        coords.y = startCoords.y + (newHeight - startRect.height) * edgeSign
      }
      else {
        const newWidth = rect.height * ratio

        coords.x = startCoords.x + (newWidth - startRect.width) * edgeSign
      }
    }

    export default makeModifier(aspectRatio, 'aspectRatio')
    export { aspectRatio }