src/tree/path.js
import { haveNoChildren } from "../utils/tree-utils";
import constants from "./size-constants";
export function getPoints(d, topId, { depthSpacing, isVertical, x, y }, navigationMode) {
// TODO: Generalize to make all directions work, currently on only ttb working
const { cardWidth, cardHeight, buttonHeight, cardPadding, buttonMargin } = constants;
const points = [];
const halfCard = { x: cardWidth / 2, y: cardHeight / 2 };
const start = { x: d.xActual, y: d.yActual };
// TODO: fix so auto mode does not get a path to parent not showing
if (d.parent && d.parent.data.id !== "Root") {
const halfDepth = depthSpacing / 2;
const yOffset = navigationMode === "expandAll" ? cardHeight : cardHeight + cardPadding + buttonHeight;
const end = { x: x(d.parent) + halfCard.x, y: y(d.parent) + yOffset };
if (navigationMode !== "expandAll" && haveNoChildren(d.parent.children)) {
// to leafs
points.push(
isVertical
? [
{ x: start.x, y: start.y + halfCard.y },
{ x: end.x - halfCard.x, y: start.y + halfCard.y },
{ x: end.x - halfCard.x, y: end.y + buttonMargin },
{ x: end.x, y: end.y + buttonMargin },
{ x: end.x, y: end.y },
]
: [
{ x: start.x, y: start.y },
{ x: start.x, y: end.y - halfCard.y },
{ x: end.x + halfDepth, y: end.y - halfCard.y },
{ x: end.x + halfDepth, y: end.y },
{ x: end.x, y: end.y },
],
);
} else if (start.x === x(d.parent) || start.y === y(d.parent)) {
// straight line
points.push([
{ x: start.x + halfCard.x, y: start.y },
{ x: end.x, y: end.y },
]);
} else {
// to nodes with children
points.push(
isVertical
? [
{ x: start.x + halfCard.x, y: start.y },
{ x: start.x + halfCard.x, y: start.y - cardPadding },
{ x: end.x, y: start.y - cardPadding },
{ x: end.x, y: end.y },
]
: [
{ x: start.x, y: start.y },
{ x: start.x - cardPadding, y: start.y },
{ x: start.x - cardPadding, y: end.y },
{ x: end.x, y: end.y },
],
);
}
} else if (d.parent) {
// to up button or dummy root
points.push([
{ x: start.x + halfCard.x, y: start.y },
{ x: start.x + halfCard.x, y: start.y - cardPadding },
]);
}
if (d.children && d.data.id !== "Root") {
// to expand button
points.push([
{ x: start.x + halfCard.x, y: start.y + cardHeight },
{ x: start.x + halfCard.x, y: start.y + cardHeight + cardPadding },
]);
}
return points;
}
export function getPath(points) {
// gets the path from first to last points, making turns with radius r at intermediate points
const { r } = constants;
let pathString = `M ${points[0].x} ${points[0].y} `;
let dir;
const setDir = (i) => {
const delta = {
x: points[i].x - points[i - 1].x,
y: points[i].y - points[i - 1].y,
};
dir = {
x: (delta.x > 0) - (delta.x < 0) || +delta.x,
y: (delta.y > 0) - (delta.y < 0) || +delta.y,
};
};
setDir(1);
for (let i = 1; i < points.length; ++i) {
const point = points[i];
if (i < points.length - 1) {
pathString += `L ${point.x - dir.x * r} ${point.y - dir.y * r} `;
setDir(i + 1);
pathString += `Q ${point.x} ${point.y} ${point.x + dir.x * r} ${point.y + dir.y * r} `;
} else {
// Don't add curve for last point
pathString += `L ${point.x} ${point.y} `;
}
}
return pathString;
}
export default function createPaths(node, positioning, topId, navigationMode) {
node
.append("path")
.attr("class", "sn-org-path")
.attr("id", (d) => d.data.id)
.attr("d", (d) => {
let path = "";
const pointSets = getPoints(d, topId, positioning, navigationMode);
pointSets.forEach((points) => {
path += getPath(points).slice(0, -1);
});
return path;
});
}