UNPKG

@mermaid-js/layout-tidy-tree

Version:

Tidy-tree layout engine for mermaid

5 lines 53.5 kB
{ "version": 3, "sources": ["../../../../../node_modules/.pnpm/non-layered-tidy-tree-layout@2.0.2/node_modules/non-layered-tidy-tree-layout/src/algorithm.js", "../../../../../node_modules/.pnpm/non-layered-tidy-tree-layout@2.0.2/node_modules/non-layered-tidy-tree-layout/src/helpers.js", "../../../src/layout.ts", "../../../src/render.ts"], "sourcesContent": ["class Tree {\n constructor(width, height, y, children) {\n this.w = width\n this.h = height\n this.y = y\n this.c = children\n this.cs = children.length\n\n this.x = 0\n this.prelim = 0\n this.mod = 0\n this.shift = 0\n this.change = 0\n this.tl = null // Left thread\n this.tr = null // Right thread\n this.el = null // extreme left nodes\n this.er = null // extreme right nodes\n //sum of modifiers at the extreme nodes\n this.msel = 0\n this.mser = 0\n }\n}\n\nfunction setExtremes(tree) {\n if (tree.cs === 0) {\n tree.el = tree\n tree.er = tree\n tree.msel = tree.mser = 0\n } else {\n tree.el = tree.c[0].el\n tree.msel = tree.c[0].msel\n tree.er = tree.c[tree.cs - 1].er\n tree.mser = tree.c[tree.cs - 1].mser\n }\n}\n\nfunction bottom(tree) {\n return tree.y + tree.h\n}\n\n/* A linked list of the indexes of left siblings and their lowest vertical coordinate.\n */\nclass IYL {\n constructor(lowY, index, next) {\n this.lowY = lowY\n this.index = index\n this.next = next\n }\n}\n\nfunction updateIYL(minY, i, ih) {\n // Remove siblings that are hidden by the new subtree.\n while (ih !== null && minY >= ih.lowY) {\n // Prepend the new subtree\n ih = ih.next\n }\n return new IYL(minY, i, ih)\n}\n\nfunction distributeExtra(tree, i, si, distance) {\n // Are there intermediate children?\n if (si !== i - 1) {\n const nr = i - si\n tree.c[si + 1].shift += distance / nr\n tree.c[i].shift -= distance / nr\n tree.c[i].change -= distance - distance / nr\n }\n}\n\nfunction moveSubtree(tree, i, si, distance) {\n // Move subtree by changing mod.\n tree.c[i].mod += distance\n tree.c[i].msel += distance\n tree.c[i].mser += distance\n distributeExtra(tree, i, si, distance)\n}\n\nfunction nextLeftContour(tree) {\n return tree.cs === 0 ? tree.tl : tree.c[0]\n}\n\nfunction nextRightContour(tree) {\n return tree.cs === 0 ? tree.tr : tree.c[tree.cs - 1]\n}\n\nfunction setLeftThread(tree, i, cl, modsumcl) {\n const li = tree.c[0].el\n li.tl = cl\n // Change mod so that the sum of modifier after following thread is correct.\n const diff = (modsumcl - cl.mod) - tree.c[0].msel\n li.mod += diff\n // Change preliminary x coordinate so that the node does not move.\n li.prelim -= diff\n // Update extreme node and its sum of modifiers.\n tree.c[0].el = tree.c[i].el\n tree.c[0].msel = tree.c[i].msel\n}\n\n// Symmetrical to setLeftThread\nfunction setRightThread(tree, i, sr, modsumsr) {\n const ri = tree.c[i].er\n ri.tr = sr\n const diff = (modsumsr - sr.mod) - tree.c[i].mser\n ri.mod += diff\n ri.prelim -= diff\n tree.c[i].er = tree.c[i - 1].er\n tree.c[i].mser = tree.c[i - 1].mser\n}\n\nfunction seperate(tree, i, ih) {\n // Right contour node of left siblings and its sum of modifiers.\n let sr = tree.c[i - 1]\n let mssr = sr.mod\n // Left contour node of right siblings and its sum of modifiers.\n let cl = tree.c[i]\n let mscl = cl.mod\n while (sr !== null && cl !== null) {\n if (bottom(sr) > ih.lowY) {\n ih = ih.next\n }\n // How far to the left of the right side of sr is the left side of cl.\n const distance = mssr + sr.prelim + sr.w - (mscl + cl.prelim)\n if (distance > 0) {\n mscl += distance\n moveSubtree(tree, i, ih.index, distance)\n }\n\n const sy = bottom(sr)\n const cy = bottom(cl)\n if (sy <= cy) {\n sr = nextRightContour(sr)\n if (sr !== null) {\n mssr += sr.mod\n }\n }\n if (sy >= cy) {\n cl = nextLeftContour(cl)\n if (cl !== null) {\n mscl += cl.mod\n }\n }\n }\n\n // Set threads and update extreme nodes.\n // In the first case, the current subtree must be taller than the left siblings.\n if (sr === null && cl !== null) {\n setLeftThread(tree, i, cl, mscl)\n } else if (sr !== null && cl === null) {\n setRightThread(tree, i, sr, mssr)\n }\n}\n\nfunction positionRoot(tree) {\n // Position root between children, taking into account their mod.\n tree.prelim =\n (tree.c[0].prelim +\n tree.c[0].mod +\n tree.c[tree.cs - 1].mod +\n tree.c[tree.cs - 1].prelim +\n tree.c[tree.cs - 1].w) /\n 2 -\n tree.w / 2\n}\n\nfunction firstWalk(tree) {\n if (tree.cs === 0) {\n setExtremes(tree)\n return\n }\n\n firstWalk(tree.c[0])\n let ih = updateIYL(bottom(tree.c[0].el), 0, null)\n for (let i = 1; i < tree.cs; i++) {\n firstWalk(tree.c[i])\n const minY = bottom(tree.c[i].er)\n seperate(tree, i, ih)\n ih = updateIYL(minY, i, ih)\n }\n positionRoot(tree)\n setExtremes(tree)\n}\n\nfunction addChildSpacing(tree) {\n let d = 0\n let modsumdelta = 0\n for (let i = 0; i < tree.cs; i++) {\n d += tree.c[i].shift\n modsumdelta += d + tree.c[i].change\n tree.c[i].mod += modsumdelta\n }\n}\n\nfunction secondWalk(tree, modsum) {\n modsum += tree.mod\n // Set absolute (no-relative) horizontal coordinates.\n tree.x = tree.prelim + modsum\n addChildSpacing(tree)\n for (let i = 0; i < tree.cs; i++) {\n secondWalk(tree.c[i], modsum)\n }\n}\n\nfunction layout(tree) {\n firstWalk(tree)\n secondWalk(tree, 0)\n}\n\nexport { Tree, layout }\n", "import { layout, Tree } from './algorithm'\n\nclass BoundingBox {\n /**\n * @param {number} gap - the gap between sibling nodes\n * @param {number} bottomPadding - the height reserved for connection drawing\n */\n constructor(gap, bottomPadding) {\n this.gap = gap\n this.bottomPadding = bottomPadding\n }\n\n addBoundingBox(width, height) {\n return { width: width + this.gap, height: height + this.bottomPadding }\n }\n\n /**\n * Return the coordinate without the bounding box for a node\n */\n removeBoundingBox(x, y) {\n return { x: x + this.gap / 2, y }\n }\n}\n\nclass Layout {\n constructor(boundingBox) {\n this.bb = boundingBox\n }\n\n /**\n * Layout treeData.\n * Return modified treeData and the bounding box encompassing all the nodes.\n * \n * See getSize() for more explanation.\n */\n layout(treeData) {\n const tree = this.convert(treeData)\n layout(tree)\n const { boundingBox, result } = this.assignLayout(tree, treeData)\n\n return { result, boundingBox }\n }\n\n /**\n * Returns Tree to layout, with bounding boxes added to each node.\n */\n convert(treeData, y = 0) {\n if (treeData === null) return null\n\n const { width, height } = this.bb.addBoundingBox(\n treeData.width,\n treeData.height\n )\n let children = []\n if (treeData.children && treeData.children.length) {\n for (let i = 0; i < treeData.children.length; i++) {\n children[i] = this.convert(treeData.children[i], y + height)\n }\n }\n\n return new Tree(width, height, y, children)\n }\n\n /**\n * Assign layout tree x, y coordinates back to treeData,\n * with bounding boxes removed.\n */\n assignCoordinates(tree, treeData) {\n const { x, y } = this.bb.removeBoundingBox(tree.x, tree.y)\n treeData.x = x\n treeData.y = y\n for (let i = 0; i < tree.c.length; i++) {\n this.assignCoordinates(tree.c[i], treeData.children[i])\n }\n }\n\n /**\n * Return the bounding box that encompasses all the nodes.\n * The result has a structure of\n * { left: number, right: number, top: number, bottom: nubmer}.\n * This is not the same bounding box concept as the `BoundingBox` class\n * used to construct `Layout` class.\n */\n getSize(treeData, box = null) {\n const { x, y, width, height } = treeData\n if (box === null) {\n box = { left: x, right: x + width, top: y, bottom: y + height }\n }\n box.left = Math.min(box.left, x)\n box.right = Math.max(box.right, x + width)\n box.top = Math.min(box.top, y)\n box.bottom = Math.max(box.bottom, y + height)\n\n if (treeData.children) {\n for (const child of treeData.children) {\n this.getSize(child, box)\n }\n }\n\n return box\n }\n\n /**\n * This function does assignCoordinates and getSize in one pass.\n */\n assignLayout(tree, treeData, box = null) {\n const { x, y } = this.bb.removeBoundingBox(tree.x, tree.y)\n treeData.x = x\n treeData.y = y\n\n const { width, height } = treeData\n if (box === null) {\n box = { left: x, right: x + width, top: y, bottom: y + height }\n }\n box.left = Math.min(box.left, x)\n box.right = Math.max(box.right, x + width)\n box.top = Math.min(box.top, y)\n box.bottom = Math.max(box.bottom, y + height)\n\n for (let i = 0; i < tree.c.length; i++) {\n this.assignLayout(tree.c[i], treeData.children[i], box)\n }\n\n return { result: treeData, boundingBox: box }\n }\n}\n\nexport { Layout, BoundingBox }\n", "import type { LayoutData } from 'mermaid';\nimport type { Bounds, Point } from 'mermaid/src/types.js';\nimport { BoundingBox, Layout } from 'non-layered-tidy-tree-layout';\nimport type {\n Edge,\n LayoutResult,\n Node,\n PositionedEdge,\n PositionedNode,\n TidyTreeNode,\n} from './types.js';\n\n/**\n * Execute the tidy-tree layout algorithm on generic layout data\n *\n * This function takes layout data and uses the non-layered-tidy-tree-layout\n * algorithm to calculate optimal node positions for tree structures.\n *\n * @param data - The layout data containing nodes, edges, and configuration\n * @param config - Mermaid configuration object\n * @returns Promise resolving to layout result with positioned nodes and edges\n */\nexport function executeTidyTreeLayout(data: LayoutData): Promise<LayoutResult> {\n let intersectionShift = 50;\n\n return new Promise((resolve, reject) => {\n try {\n if (!data.nodes || !Array.isArray(data.nodes) || data.nodes.length === 0) {\n throw new Error('No nodes found in layout data');\n }\n\n if (!data.edges || !Array.isArray(data.edges)) {\n data.edges = [];\n }\n\n const { leftTree, rightTree, rootNode } = convertToDualTreeFormat(data);\n\n const gap = 20;\n const bottomPadding = 40;\n intersectionShift = 30;\n\n const bb = new BoundingBox(gap, bottomPadding);\n const layout = new Layout(bb);\n\n let leftResult = null;\n let rightResult = null;\n\n if (leftTree) {\n const leftLayoutResult = layout.layout(leftTree);\n leftResult = leftLayoutResult.result;\n }\n\n if (rightTree) {\n const rightLayoutResult = layout.layout(rightTree);\n rightResult = rightLayoutResult.result;\n }\n\n const positionedNodes = combineAndPositionTrees(rootNode, leftResult, rightResult);\n const positionedEdges = calculateEdgePositions(\n data.edges,\n positionedNodes,\n intersectionShift\n );\n resolve({\n nodes: positionedNodes,\n edges: positionedEdges,\n });\n } catch (error) {\n reject(error);\n }\n });\n}\n\n/**\n * Convert LayoutData to dual-tree format (left and right trees)\n *\n * This function builds two separate tree structures from the nodes and edges,\n * alternating children between left and right trees.\n */\nfunction convertToDualTreeFormat(data: LayoutData): {\n leftTree: TidyTreeNode | null;\n rightTree: TidyTreeNode | null;\n rootNode: TidyTreeNode;\n} {\n const { nodes, edges } = data;\n\n const nodeMap = new Map<string, Node>();\n nodes.forEach((node) => nodeMap.set(node.id, node));\n\n const children = new Map<string, string[]>();\n const parents = new Map<string, string>();\n\n edges.forEach((edge) => {\n const parentId = edge.start;\n const childId = edge.end;\n\n if (parentId && childId) {\n if (!children.has(parentId)) {\n children.set(parentId, []);\n }\n children.get(parentId)!.push(childId);\n parents.set(childId, parentId);\n }\n });\n\n const rootNodeData = nodes.find((node) => !parents.has(node.id));\n if (!rootNodeData && nodes.length === 0) {\n throw new Error('No nodes available to create tree');\n }\n\n const actualRoot = rootNodeData ?? nodes[0];\n\n const rootNode: TidyTreeNode = {\n id: actualRoot.id,\n width: actualRoot.width ?? 100,\n height: actualRoot.height ?? 50,\n _originalNode: actualRoot,\n };\n\n const rootChildren = children.get(actualRoot.id) ?? [];\n const leftChildren: string[] = [];\n const rightChildren: string[] = [];\n\n rootChildren.forEach((childId, index) => {\n if (index % 2 === 0) {\n leftChildren.push(childId);\n } else {\n rightChildren.push(childId);\n }\n });\n\n const leftTree = leftChildren.length > 0 ? buildSubTree(leftChildren, children, nodeMap) : null;\n\n const rightTree =\n rightChildren.length > 0 ? buildSubTree(rightChildren, children, nodeMap) : null;\n\n return { leftTree, rightTree, rootNode };\n}\n\n/**\n * Build a subtree from a list of root children\n * For horizontal trees, we need to transpose width/height since the tree will be rotated 90\u00B0\n */\nfunction buildSubTree(\n rootChildren: string[],\n children: Map<string, string[]>,\n nodeMap: Map<string, Node>\n): TidyTreeNode {\n const virtualRoot: TidyTreeNode = {\n id: `virtual-root-${Math.random()}`,\n width: 1,\n height: 1,\n children: rootChildren\n .map((childId) => nodeMap.get(childId))\n .filter((child): child is Node => child !== undefined)\n .map((child) => convertNodeToTidyTreeTransposed(child, children, nodeMap)),\n };\n\n return virtualRoot;\n}\n\n/**\n * Recursively convert a node and its children to tidy-tree format\n * This version transposes width/height for horizontal tree layout\n */\nfunction convertNodeToTidyTreeTransposed(\n node: Node,\n children: Map<string, string[]>,\n nodeMap: Map<string, Node>\n): TidyTreeNode {\n const childIds = children.get(node.id) ?? [];\n const childNodes = childIds\n .map((childId) => nodeMap.get(childId))\n .filter((child): child is Node => child !== undefined)\n .map((child) => convertNodeToTidyTreeTransposed(child, children, nodeMap));\n\n return {\n id: node.id,\n width: node.height ?? 50,\n height: node.width ?? 100,\n children: childNodes.length > 0 ? childNodes : undefined,\n _originalNode: node,\n };\n}\n/**\n * Combine and position the left and right trees around the root node\n * Creates a bidirectional layout where left tree grows left and right tree grows right\n */\nfunction combineAndPositionTrees(\n rootNode: TidyTreeNode,\n leftResult: TidyTreeNode | null,\n rightResult: TidyTreeNode | null\n): PositionedNode[] {\n const positionedNodes: PositionedNode[] = [];\n\n const rootX = 0;\n const rootY = 0;\n\n const treeSpacing = rootNode.width / 2 + 30;\n const leftTreeNodes: PositionedNode[] = [];\n const rightTreeNodes: PositionedNode[] = [];\n\n if (leftResult?.children) {\n positionLeftTreeBidirectional(leftResult.children, leftTreeNodes, rootX - treeSpacing, rootY);\n }\n\n if (rightResult?.children) {\n positionRightTreeBidirectional(\n rightResult.children,\n rightTreeNodes,\n rootX + treeSpacing,\n rootY\n );\n }\n\n let leftTreeCenterY = 0;\n let rightTreeCenterY = 0;\n\n if (leftTreeNodes.length > 0) {\n const leftTreeXPositions = [...new Set(leftTreeNodes.map((node) => node.x))].sort(\n (a, b) => b - a\n );\n const firstLevelLeftX = leftTreeXPositions[0];\n const firstLevelLeftNodes = leftTreeNodes.filter((node) => node.x === firstLevelLeftX);\n\n if (firstLevelLeftNodes.length > 0) {\n const leftMinY = Math.min(\n ...firstLevelLeftNodes.map((node) => node.y - (node.height ?? 50) / 2)\n );\n const leftMaxY = Math.max(\n ...firstLevelLeftNodes.map((node) => node.y + (node.height ?? 50) / 2)\n );\n leftTreeCenterY = (leftMinY + leftMaxY) / 2;\n }\n }\n\n if (rightTreeNodes.length > 0) {\n const rightTreeXPositions = [...new Set(rightTreeNodes.map((node) => node.x))].sort(\n (a, b) => a - b\n );\n const firstLevelRightX = rightTreeXPositions[0];\n const firstLevelRightNodes = rightTreeNodes.filter((node) => node.x === firstLevelRightX);\n\n if (firstLevelRightNodes.length > 0) {\n const rightMinY = Math.min(\n ...firstLevelRightNodes.map((node) => node.y - (node.height ?? 50) / 2)\n );\n const rightMaxY = Math.max(\n ...firstLevelRightNodes.map((node) => node.y + (node.height ?? 50) / 2)\n );\n rightTreeCenterY = (rightMinY + rightMaxY) / 2;\n }\n }\n\n const leftTreeOffset = -leftTreeCenterY;\n const rightTreeOffset = -rightTreeCenterY;\n\n positionedNodes.push({\n id: String(rootNode.id),\n x: rootX,\n y: rootY + 20,\n section: 'root',\n width: rootNode._originalNode?.width ?? rootNode.width,\n height: rootNode._originalNode?.height ?? rootNode.height,\n originalNode: rootNode._originalNode,\n });\n\n const leftTreeNodesWithOffset = leftTreeNodes.map((node) => ({\n id: node.id,\n x: node.x - (node.width ?? 0) / 2,\n y: node.y + leftTreeOffset + (node.height ?? 0) / 2,\n section: 'left' as const,\n width: node.width,\n height: node.height,\n originalNode: node.originalNode,\n }));\n\n const rightTreeNodesWithOffset = rightTreeNodes.map((node) => ({\n id: node.id,\n x: node.x + (node.width ?? 0) / 2,\n y: node.y + rightTreeOffset + (node.height ?? 0) / 2,\n section: 'right' as const,\n width: node.width,\n height: node.height,\n originalNode: node.originalNode,\n }));\n\n positionedNodes.push(...leftTreeNodesWithOffset);\n positionedNodes.push(...rightTreeNodesWithOffset);\n\n return positionedNodes;\n}\n\n/**\n * Position nodes from the left tree in a bidirectional layout (grows to the left)\n * Rotates the tree 90 degrees counterclockwise so it grows horizontally to the left\n */\nfunction positionLeftTreeBidirectional(\n nodes: TidyTreeNode[],\n positionedNodes: PositionedNode[],\n offsetX: number,\n offsetY: number\n): void {\n nodes.forEach((node) => {\n const distanceFromRoot = node.y ?? 0;\n const verticalPosition = node.x ?? 0;\n\n const originalWidth = node._originalNode?.width ?? 100;\n const originalHeight = node._originalNode?.height ?? 50;\n\n const adjustedY = offsetY + verticalPosition;\n\n positionedNodes.push({\n id: String(node.id),\n x: offsetX - distanceFromRoot,\n y: adjustedY,\n width: originalWidth,\n height: originalHeight,\n originalNode: node._originalNode,\n });\n\n if (node.children) {\n positionLeftTreeBidirectional(node.children, positionedNodes, offsetX, offsetY);\n }\n });\n}\n\n/**\n * Position nodes from the right tree in a bidirectional layout (grows to the right)\n * Rotates the tree 90 degrees clockwise so it grows horizontally to the right\n */\nfunction positionRightTreeBidirectional(\n nodes: TidyTreeNode[],\n positionedNodes: PositionedNode[],\n offsetX: number,\n offsetY: number\n): void {\n nodes.forEach((node) => {\n const distanceFromRoot = node.y ?? 0;\n const verticalPosition = node.x ?? 0;\n\n const originalWidth = node._originalNode?.width ?? 100;\n const originalHeight = node._originalNode?.height ?? 50;\n\n const adjustedY = offsetY + verticalPosition;\n\n positionedNodes.push({\n id: String(node.id),\n x: offsetX + distanceFromRoot,\n y: adjustedY,\n width: originalWidth,\n height: originalHeight,\n originalNode: node._originalNode,\n });\n\n if (node.children) {\n positionRightTreeBidirectional(node.children, positionedNodes, offsetX, offsetY);\n }\n });\n}\n\n/**\n * Calculate the intersection point of a line with a circle\n * @param circle - Circle coordinates and radius\n * @param lineStart - Starting point of the line\n * @param lineEnd - Ending point of the line\n * @returns The intersection point\n */\nfunction computeCircleEdgeIntersection(circle: Bounds, lineStart: Point, lineEnd: Point): Point {\n const radius = Math.min(circle.width, circle.height) / 2;\n\n const dx = lineEnd.x - lineStart.x;\n const dy = lineEnd.y - lineStart.y;\n const length = Math.sqrt(dx * dx + dy * dy);\n\n if (length === 0) {\n return lineStart;\n }\n\n const nx = dx / length;\n const ny = dy / length;\n\n return {\n x: circle.x - nx * radius,\n y: circle.y - ny * radius,\n };\n}\n\nfunction intersection(node: PositionedNode, outsidePoint: Point, insidePoint: Point): Point {\n const x = node.x;\n const y = node.y;\n\n if (!node.width || !node.height) {\n return { x: outsidePoint.x, y: outsidePoint.y };\n }\n const dx = Math.abs(x - insidePoint.x);\n const w = node?.width / 2;\n let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;\n const h = node.height / 2;\n\n const Q = Math.abs(outsidePoint.y - insidePoint.y);\n const R = Math.abs(outsidePoint.x - insidePoint.x);\n\n if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {\n // Intersection is top or bottom of rect.\n const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;\n r = (R * q) / Q;\n const res = {\n x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,\n y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,\n };\n\n if (r === 0) {\n res.x = outsidePoint.x;\n res.y = outsidePoint.y;\n }\n if (R === 0) {\n res.x = outsidePoint.x;\n }\n if (Q === 0) {\n res.y = outsidePoint.y;\n }\n\n return res;\n } else {\n if (insidePoint.x < outsidePoint.x) {\n r = outsidePoint.x - w - x;\n } else {\n r = x - w - outsidePoint.x;\n }\n const q = (Q * r) / R;\n let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;\n let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;\n\n if (r === 0) {\n _x = outsidePoint.x;\n _y = outsidePoint.y;\n }\n if (R === 0) {\n _x = outsidePoint.x;\n }\n if (Q === 0) {\n _y = outsidePoint.y;\n }\n\n return { x: _x, y: _y };\n }\n}\n\n/**\n * Calculate edge positions based on positioned nodes\n * Now includes tree membership and node dimensions for precise edge calculations\n * Edges now stop at shape boundaries instead of extending to centers\n */\nfunction calculateEdgePositions(\n edges: Edge[],\n positionedNodes: PositionedNode[],\n intersectionShift: number\n): PositionedEdge[] {\n const nodeInfo = new Map<string, PositionedNode>();\n positionedNodes.forEach((node) => {\n nodeInfo.set(node.id, node);\n });\n\n return edges.map((edge) => {\n const sourceNode = nodeInfo.get(edge.start ?? '');\n const targetNode = nodeInfo.get(edge.end ?? '');\n\n if (!sourceNode || !targetNode) {\n return {\n id: edge.id,\n source: edge.start ?? '',\n target: edge.end ?? '',\n startX: 0,\n startY: 0,\n midX: 0,\n midY: 0,\n endX: 0,\n endY: 0,\n points: [{ x: 0, y: 0 }],\n sourceSection: undefined,\n targetSection: undefined,\n sourceWidth: undefined,\n sourceHeight: undefined,\n targetWidth: undefined,\n targetHeight: undefined,\n };\n }\n\n const sourceCenter = { x: sourceNode.x, y: sourceNode.y };\n const targetCenter = { x: targetNode.x, y: targetNode.y };\n\n const isSourceRound = ['circle', 'cloud', 'bang'].includes(\n sourceNode.originalNode?.shape ?? ''\n );\n const isTargetRound = ['circle', 'cloud', 'bang'].includes(\n targetNode.originalNode?.shape ?? ''\n );\n\n let startPos = isSourceRound\n ? computeCircleEdgeIntersection(\n {\n x: sourceNode.x,\n y: sourceNode.y,\n width: sourceNode.width ?? 100,\n height: sourceNode.height ?? 100,\n },\n targetCenter,\n sourceCenter\n )\n : intersection(sourceNode, sourceCenter, targetCenter);\n\n let endPos = isTargetRound\n ? computeCircleEdgeIntersection(\n {\n x: targetNode.x,\n y: targetNode.y,\n width: targetNode.width ?? 100,\n height: targetNode.height ?? 100,\n },\n sourceCenter,\n targetCenter\n )\n : intersection(targetNode, targetCenter, sourceCenter);\n\n const midX = (startPos.x + endPos.x) / 2;\n const midY = (startPos.y + endPos.y) / 2;\n\n const points = [startPos];\n if (sourceNode.section === 'left') {\n points.push({\n x: sourceNode.x - (sourceNode.width ?? 0) / 2 - intersectionShift,\n y: sourceNode.y,\n });\n } else if (sourceNode.section === 'right') {\n points.push({\n x: sourceNode.x + (sourceNode.width ?? 0) / 2 + intersectionShift,\n y: sourceNode.y,\n });\n }\n if (targetNode.section === 'left') {\n points.push({\n x: targetNode.x + (targetNode.width ?? 0) / 2 + intersectionShift,\n y: targetNode.y,\n });\n } else if (targetNode.section === 'right') {\n points.push({\n x: targetNode.x - (targetNode.width ?? 0) / 2 - intersectionShift,\n y: targetNode.y,\n });\n }\n\n points.push(endPos);\n\n const secondPoint = points.length > 1 ? points[1] : targetCenter;\n startPos = isSourceRound\n ? computeCircleEdgeIntersection(\n {\n x: sourceNode.x,\n y: sourceNode.y,\n width: sourceNode.width ?? 100,\n height: sourceNode.height ?? 100,\n },\n secondPoint,\n sourceCenter\n )\n : intersection(sourceNode, secondPoint, sourceCenter);\n points[0] = startPos;\n\n const secondLastPoint = points.length > 1 ? points[points.length - 2] : sourceCenter;\n endPos = isTargetRound\n ? computeCircleEdgeIntersection(\n {\n x: targetNode.x,\n y: targetNode.y,\n width: targetNode.width ?? 100,\n height: targetNode.height ?? 100,\n },\n secondLastPoint,\n targetCenter\n )\n : intersection(targetNode, secondLastPoint, targetCenter);\n points[points.length - 1] = endPos;\n\n return {\n id: edge.id,\n source: edge.start ?? '',\n target: edge.end ?? '',\n startX: startPos.x,\n startY: startPos.y,\n midX,\n midY,\n endX: endPos.x,\n endY: endPos.y,\n points,\n sourceSection: sourceNode?.section,\n targetSection: targetNode?.section,\n sourceWidth: sourceNode?.width,\n sourceHeight: sourceNode?.height,\n targetWidth: targetNode?.width,\n targetHeight: targetNode?.height,\n };\n });\n}\n\n/**\n * Validate layout data structure\n * @param data - The data to validate\n * @returns True if data is valid, throws error otherwise\n */\nexport function validateLayoutData(data: LayoutData): boolean {\n if (!data) {\n throw new Error('Layout data is required');\n }\n\n if (!data.config) {\n throw new Error('Configuration is required in layout data');\n }\n\n if (!Array.isArray(data.nodes)) {\n throw new Error('Nodes array is required in layout data');\n }\n\n if (!Array.isArray(data.edges)) {\n throw new Error('Edges array is required in layout data');\n }\n\n return true;\n}\n", "import type { InternalHelpers, LayoutData, RenderOptions, SVG } from 'mermaid';\nimport { executeTidyTreeLayout } from './layout.js';\n\ninterface NodeWithPosition {\n id: string;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n domId?: any;\n [key: string]: any;\n}\n\n/**\n * Render function for bidirectional tidy-tree layout algorithm\n *\n * This follows the same pattern as ELK and dagre renderers:\n * 1. Insert nodes into DOM to get their actual dimensions\n * 2. Run the bidirectional tidy-tree layout algorithm to calculate positions\n * 3. Position the nodes and edges based on layout results\n *\n * The bidirectional layout creates two trees that grow horizontally in opposite\n * directions from a central root node:\n * - Left tree: grows horizontally to the left (children: 1st, 3rd, 5th...)\n * - Right tree: grows horizontally to the right (children: 2nd, 4th, 6th...)\n */\nexport const render = async (\n data4Layout: LayoutData,\n svg: SVG,\n {\n insertCluster,\n insertEdge,\n insertEdgeLabel,\n insertMarkers,\n insertNode,\n log,\n positionEdgeLabel,\n }: InternalHelpers,\n { algorithm: _algorithm }: RenderOptions\n) => {\n const nodeDb: Record<string, NodeWithPosition> = {};\n const clusterDb: Record<string, any> = {};\n\n const element = svg.select('g');\n insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId);\n\n const subGraphsEl = element.insert('g').attr('class', 'subgraphs');\n const edgePaths = element.insert('g').attr('class', 'edgePaths');\n const edgeLabels = element.insert('g').attr('class', 'edgeLabels');\n const nodes = element.insert('g').attr('class', 'nodes');\n // Step 1: Insert nodes into DOM to get their actual dimensions\n log.debug('Inserting nodes into DOM for dimension calculation');\n\n await Promise.all(\n data4Layout.nodes.map(async (node) => {\n if (node.isGroup) {\n const clusterNode: NodeWithPosition = {\n ...node,\n id: node.id,\n width: node.width,\n height: node.height,\n };\n clusterDb[node.id] = clusterNode;\n nodeDb[node.id] = clusterNode;\n\n await insertCluster(subGraphsEl, node);\n } else {\n const nodeWithPosition: NodeWithPosition = {\n ...node,\n id: node.id,\n width: node.width,\n height: node.height,\n };\n nodeDb[node.id] = nodeWithPosition;\n\n const nodeEl = await insertNode(nodes, node, {\n config: data4Layout.config,\n dir: data4Layout.direction || 'TB',\n });\n\n const boundingBox = nodeEl.node()!.getBBox();\n nodeWithPosition.width = boundingBox.width;\n nodeWithPosition.height = boundingBox.height;\n nodeWithPosition.domId = nodeEl;\n\n log.debug(`Node ${node.id} dimensions: ${boundingBox.width}x${boundingBox.height}`);\n }\n })\n );\n // Step 2: Run the bidirectional tidy-tree layout algorithm\n log.debug('Running bidirectional tidy-tree layout algorithm');\n\n const updatedLayoutData = {\n ...data4Layout,\n nodes: data4Layout.nodes.map((node) => {\n const nodeWithDimensions = nodeDb[node.id];\n return {\n ...node,\n width: nodeWithDimensions.width ?? node.width ?? 100,\n height: nodeWithDimensions.height ?? node.height ?? 50,\n };\n }),\n };\n\n const layoutResult = await executeTidyTreeLayout(updatedLayoutData);\n // Step 3: Position the nodes based on bidirectional layout results\n log.debug('Positioning nodes based on bidirectional layout results');\n\n layoutResult.nodes.forEach((positionedNode) => {\n const node = nodeDb[positionedNode.id];\n if (node?.domId) {\n // Position the node at the calculated coordinates from bidirectional layout\n // The layout algorithm has already calculated positions for:\n // - Root node at center (0, 0)\n // - Left tree nodes with negative x coordinates (growing left)\n // - Right tree nodes with positive x coordinates (growing right)\n node.domId.attr('transform', `translate(${positionedNode.x}, ${positionedNode.y})`);\n // Store the final position\n node.x = positionedNode.x;\n node.y = positionedNode.y;\n // Step 3: Position the nodes based on bidirectional layout results\n log.debug(`Positioned node ${node.id} at (${positionedNode.x}, ${positionedNode.y})`);\n }\n });\n\n log.debug('Inserting and positioning edges');\n\n await Promise.all(\n data4Layout.edges.map(async (edge) => {\n await insertEdgeLabel(edgeLabels, edge);\n\n const startNode = nodeDb[edge.start ?? ''];\n const endNode = nodeDb[edge.end ?? ''];\n\n if (startNode && endNode) {\n const positionedEdge = layoutResult.edges.find((e) => e.id === edge.id);\n\n if (positionedEdge) {\n log.debug('APA01 positionedEdge', positionedEdge);\n const edgeWithPath = {\n ...edge,\n points: positionedEdge.points,\n };\n const paths = insertEdge(\n edgePaths,\n edgeWithPath,\n clusterDb,\n data4Layout.type,\n startNode,\n endNode,\n data4Layout.diagramId\n );\n\n positionEdgeLabel(edgeWithPath, paths);\n } else {\n const edgeWithPath = {\n ...edge,\n points: [\n { x: startNode.x ?? 0, y: startNode.y ?? 0 },\n { x: endNode.x ?? 0, y: endNode.y ?? 0 },\n ],\n };\n\n const paths = insertEdge(\n edgePaths,\n edgeWithPath,\n clusterDb,\n data4Layout.type,\n startNode,\n endNode,\n data4Layout.diagramId\n );\n positionEdgeLabel(edgeWithPath, paths);\n }\n }\n })\n );\n\n log.debug('Bidirectional tidy-tree rendering completed');\n};\n"], "mappings": "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