export function getPathIntersect()

in src/helpers/intersect-helpers.js [106:206]


export function getPathIntersect(
  defSvgPathElement: any,
  src?: any,
  trg?: any,
  includesArrow?: boolean = true,
  viewWrapperElem: HTMLDivElement
) {
  const response = getDefaultIntersectResponse();
  const arrowSize = getArrowSize(viewWrapperElem);
  // get the rectangular area around path
  const clientRect = defSvgPathElement.getBoundingClientRect();
  // getBoundingClientRect doesn't work on Firefox.
  // We do have the width and height on the parent <symbol> element for the node,
  // so we'll use that.
  const w =
    clientRect.width || defSvgPathElement.parentElement.getAttribute('width');
  const h =
    clientRect.height || defSvgPathElement.parentElement.getAttribute('height');
  const trgX = trg?.x || 0;
  const trgY = trg?.y || 0;
  const srcX = src?.x || 0;
  const srcY = src?.y || 0;

  // calculate the positions of each corner relative to the trg position
  const top = trgY - h / 2;
  const bottom = trgY + h / 2;
  const left = trgX - w / 2;
  const right = trgX + w / 2;

  // modify the d property to add top and left to the x and y positions
  let d = defSvgPathElement.getAttribute('d');

  if (!/^M/.test(d)) {
    // doesn't look like what we expect.
    // TODO: add more use cases than simple moveTo commands
    return;
  }

  d = d.replace(/^M /, '');
  let dArr = d.split(/[ ,]+/);

  dArr = dArr.map((val, index) => {
    let isEnd = false;

    if (/Z$/.test(val)) {
      val = val.replace(/Z$/, '');
      isEnd = true;
    }

    // index % 2 are x positions
    if (index % 2 === 0) {
      return parseFloat(val) + left + (isEnd ? 'Z' : '');
    }

    return parseFloat(val) + top + (isEnd ? 'Z' : '');
  });

  const pathIntersect = intersect(
    shape('path', { d: 'M ' + dArr.join(' ') }),
    shape('line', { x1: srcX, y1: srcY, x2: trgX, y2: trgY })
  );

  if (pathIntersect.points.length > 0) {
    let arrowWidth = 0; //arrowSize.width;
    let arrowHeight = 0; //arrowSize.height;
    const xIntersect = pathIntersect.points[0].x;
    const yIntersect = pathIntersect.points[0].y;
    let multiplier = 1;

    if (xIntersect > left && xIntersect < right) {
      const yIntersectDiff = yIntersect - trgY;

      multiplier = yIntersect < trgY ? -1 : 1;

      arrowHeight = arrowSize.height * multiplier;
      // Math.min is used to find a percentage of the arrow size
      // as the arrow approaches a horizontal or vertical vector
      // Math.abs is used to force the diff to be positive,
      // because we're using a multiplier instead and Math.min would choose a large
      // negative number as the minimum, which is undesirable.
      arrowHeight = arrowHeight * Math.min(Math.abs(yIntersectDiff), 1);
    }

    if (yIntersect > top && yIntersect < bottom) {
      const xIntersectDiff = xIntersect - trgX;

      multiplier = xIntersect < trgX ? -1 : 1;

      arrowWidth = arrowSize.width * multiplier;
      arrowWidth = arrowWidth * Math.min(Math.abs(xIntersectDiff), 1);
    }

    response.xOff = trgX - xIntersect - (includesArrow ? arrowWidth / 1.25 : 0);
    response.yOff =
      trgY - yIntersect - (includesArrow ? arrowHeight / 1.25 : 0);

    response.intersect = pathIntersect.points[0];
  }

  return response;
}