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;
}