UNPKG

mermaid

Version:

Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.

1,312 lines (1,291 loc) 221 kB
import { at } from "./chunk-PLCLPJVV.mjs"; import { compileStyles, solidStateFill, styles2String, userNodeOverrides } from "./chunk-BNCO5QFQ.mjs"; import { configureLabelImages, getSubGraphTitleMargins } from "./chunk-3UJ2IBUM.mjs"; import { createText } from "./chunk-JCJHR2HC.mjs"; import { getIconSVG } from "./chunk-4PNYCQBS.mjs"; import { calculateTextWidth, decodeEntities, handleUndefinedAttr } from "./chunk-MMGVDTGO.mjs"; import { evaluate, getConfig, getConfig2, getEffectiveHtmlLabels, hasKatex, parseGenericTypes, sanitizeText, sanitizeText2 } from "./chunk-CHAKFXHA.mjs"; import { log, select_default } from "./chunk-FO5PYUIK.mjs"; import { __name } from "./chunk-PTVI3W5X.mjs"; // src/rendering-util/rendering-elements/shapes/util.ts var labelHelper = /* @__PURE__ */ __name(async (parent, node, _classes) => { let cssClasses; const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig2()?.htmlLabels); if (!_classes) { cssClasses = "node default"; } else { cssClasses = _classes; } const shapeSvg = parent.insert("g").attr("class", cssClasses).attr("id", node.domId || node.id); const labelEl = shapeSvg.insert("g").attr("class", "label").attr("style", handleUndefinedAttr(node.labelStyle)); let label; if (node.label === void 0) { label = ""; } else { label = typeof node.label === "string" ? node.label : node.label[0]; } const addBackground = !!node.icon || !!node.img; const isMarkdown = node.labelType === "markdown"; const text2 = await createText( labelEl, sanitizeText(decodeEntities(label), getConfig2()), { useHtmlLabels, width: node.width || getConfig2().flowchart?.wrappingWidth, classes: isMarkdown ? "markdown-node-label" : "", style: node.labelStyle, addSvgBackground: addBackground, markdown: isMarkdown }, getConfig2() ); let bbox = text2.getBBox(); const halfPadding = (node?.padding ?? 0) / 2; if (useHtmlLabels) { const div = text2.children[0]; const dv = select_default(text2); await configureLabelImages(div, label); bbox = div.getBoundingClientRect(); dv.attr("width", bbox.width); dv.attr("height", bbox.height); } if (useHtmlLabels) { labelEl.attr("transform", "translate(" + -bbox.width / 2 + ", " + -bbox.height / 2 + ")"); } else { labelEl.attr("transform", "translate(0, " + -bbox.height / 2 + ")"); } if (node.centerLabel) { labelEl.attr("transform", "translate(" + -bbox.width / 2 + ", " + -bbox.height / 2 + ")"); } labelEl.insert("rect", ":first-child"); return { shapeSvg, bbox, halfPadding, label: labelEl }; }, "labelHelper"); var insertLabel = /* @__PURE__ */ __name(async (parent, label, options) => { const useHtmlLabels = options.useHtmlLabels ?? getEffectiveHtmlLabels(getConfig2()); const labelEl = parent.insert("g").attr("class", "label").attr("style", options.labelStyle || ""); const text2 = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig2()), { useHtmlLabels, width: options.width || getConfig2()?.flowchart?.wrappingWidth, style: options.labelStyle, addSvgBackground: !!options.icon || !!options.img }); let bbox = text2.getBBox(); const halfPadding = options.padding / 2; if (getEffectiveHtmlLabels(getConfig2())) { const div = text2.children[0]; const dv = select_default(text2); bbox = div.getBoundingClientRect(); dv.attr("width", bbox.width); dv.attr("height", bbox.height); } if (useHtmlLabels) { labelEl.attr("transform", "translate(" + -bbox.width / 2 + ", " + -bbox.height / 2 + ")"); } else { labelEl.attr("transform", "translate(0, " + -bbox.height / 2 + ")"); } if (options.centerLabel) { labelEl.attr("transform", "translate(" + -bbox.width / 2 + ", " + -bbox.height / 2 + ")"); } labelEl.insert("rect", ":first-child"); return { shapeSvg: parent, bbox, halfPadding, label: labelEl }; }, "insertLabel"); var updateNodeBounds = /* @__PURE__ */ __name((node, element) => { const bbox = element.node().getBBox(); node.width = bbox.width; node.height = bbox.height; }, "updateNodeBounds"); var getNodeClasses = /* @__PURE__ */ __name((node, extra) => (node.look === "handDrawn" ? "rough-node" : "node") + " " + node.cssClasses + " " + (extra || ""), "getNodeClasses"); function createPathFromPoints(points) { const pointStrings = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`); pointStrings.push("Z"); return pointStrings.join(" "); } __name(createPathFromPoints, "createPathFromPoints"); function generateFullSineWavePoints(x1, y1, x2, y2, amplitude, numCycles) { const points = []; const steps = 50; const deltaX = x2 - x1; const deltaY = y2 - y1; const cycleLength = deltaX / numCycles; const frequency = 2 * Math.PI / cycleLength; const midY = y1 + deltaY / 2; for (let i = 0; i <= steps; i++) { const t = i / steps; const x = x1 + t * deltaX; const y = midY + amplitude * Math.sin(frequency * (x - x1)); points.push({ x, y }); } return points; } __name(generateFullSineWavePoints, "generateFullSineWavePoints"); function generateCirclePoints(centerX, centerY, radius, numPoints, startAngle, endAngle) { const points = []; const startAngleRad = startAngle * Math.PI / 180; const endAngleRad = endAngle * Math.PI / 180; const angleRange = endAngleRad - startAngleRad; const angleStep = angleRange / (numPoints - 1); for (let i = 0; i < numPoints; i++) { const angle = startAngleRad + i * angleStep; const x = centerX + radius * Math.cos(angle); const y = centerY + radius * Math.sin(angle); points.push({ x: -x, y: -y }); } return points; } __name(generateCirclePoints, "generateCirclePoints"); function mergePaths(roughElement) { const paths = Array.from(roughElement.childNodes).filter( (node) => node.tagName === "path" ); const mergedPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); const combinedPathData = paths.map((path) => path.getAttribute("d")).filter((d) => d !== null).join(" "); mergedPath.setAttribute("d", combinedPathData); const fillPath = paths.find((path) => path.getAttribute("fill") !== "none"); const strokePath = paths.find((path) => path.getAttribute("stroke") !== "none"); const getAttr = /* @__PURE__ */ __name((element, attr) => { return element?.getAttribute(attr) ?? void 0; }, "getAttr"); if (fillPath) { const fillAttrs = { fill: getAttr(fillPath, "fill"), "fill-opacity": getAttr(fillPath, "fill-opacity") ?? "1" }; Object.entries(fillAttrs).forEach(([attr, value]) => { if (value) { mergedPath.setAttribute(attr, value); } }); } if (strokePath) { const strokeAttrs = { stroke: getAttr(strokePath, "stroke"), "stroke-width": getAttr(strokePath, "stroke-width") ?? "1", "stroke-opacity": getAttr(strokePath, "stroke-opacity") ?? "1" }; Object.entries(strokeAttrs).forEach(([attr, value]) => { if (value) { mergedPath.setAttribute(attr, value); } }); } const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); group.appendChild(mergedPath); return group; } __name(mergePaths, "mergePaths"); // src/rendering-util/rendering-elements/intersect/intersect-rect.js var intersectRect = /* @__PURE__ */ __name((node, point) => { var x = node.x; var y = node.y; var dx = point.x - x; var dy = point.y - y; var w = node.width / 2; var h = node.height / 2; var sx, sy; if (Math.abs(dy) * w > Math.abs(dx) * h) { if (dy < 0) { h = -h; } sx = dy === 0 ? 0 : h * dx / dy; sy = h; } else { if (dx < 0) { w = -w; } sx = w; sy = dx === 0 ? 0 : w * dy / dx; } return { x: x + sx, y: y + sy }; }, "intersectRect"); var intersect_rect_default = intersectRect; // src/rendering-util/rendering-elements/createLabel.js var createLabel = /* @__PURE__ */ __name(async (element, _vertexText, style, isTitle = false, isNode = false) => { let vertexText = _vertexText || ""; if (typeof vertexText === "object") { vertexText = vertexText[0]; } const config = getConfig2(); const useHtmlLabels = getEffectiveHtmlLabels(config); return await createText( element, vertexText, { style, isTitle, useHtmlLabels, markdown: false, isNode, width: Number.POSITIVE_INFINITY }, config ); }, "createLabel"); var createLabel_default = createLabel; // src/rendering-util/rendering-elements/shapes/roundedRectPath.ts var createRoundedRectPathD = /* @__PURE__ */ __name((x, y, totalWidth, totalHeight, radius) => [ "M", x + radius, y, // Move to the first point "H", x + totalWidth - radius, // Draw horizontal line to the beginning of the right corner "A", radius, radius, 0, 0, 1, x + totalWidth, y + radius, // Draw arc to the right top corner "V", y + totalHeight - radius, // Draw vertical line down to the beginning of the right bottom corner "A", radius, radius, 0, 0, 1, x + totalWidth - radius, y + totalHeight, // Draw arc to the right bottom corner "H", x + radius, // Draw horizontal line to the beginning of the left bottom corner "A", radius, radius, 0, 0, 1, x, y + totalHeight - radius, // Draw arc to the left bottom corner "V", y + radius, // Draw vertical line up to the beginning of the left top corner "A", radius, radius, 0, 0, 1, x + radius, y, // Draw arc to the left top corner "Z" // Close the path ].join(" "), "createRoundedRectPathD"); // src/rendering-util/rendering-elements/clusters/swimlane.js var swimlane = /* @__PURE__ */ __name(async (parent, node) => { const siteConfig = getConfig2(); const { themeVariables, handDrawnSeed } = siteConfig; const { clusterBkg, clusterBorder } = themeVariables; const laneStroke = clusterBorder; const { labelStyles, nodeStyles, borderStyles, backgroundStyles } = styles2String(node); const shapeSvg = parent.insert("g").attr("class", "cluster swimlane " + (node.cssClasses || "")).attr("id", node.id).attr("data-id", node.id).attr("data-et", "cluster").attr("data-look", node.look); const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels); const isLR = node.direction === "LR"; const labelEl = shapeSvg.insert("g").attr("class", "cluster-label swimlane-label"); const text2 = await createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels, isNode: true, width: node.width }); let bbox = text2.getBBox(); if (useHtmlLabels) { const div = text2.children[0]; const dv = select_default(text2); bbox = div.getBoundingClientRect(); dv.attr("width", bbox.width); dv.attr("height", bbox.height); } const padding = node.padding ?? 0; const width = node.width <= bbox.width + padding ? bbox.width + padding : node.width; if (node.width <= bbox.width + padding) { node.diff = (width - node.width) / 2 - padding; } else { node.diff = -padding; } const height = node.height; const laneTop = node.y - height / 2; const laneBottom = node.y + height / 2; const laneLeft = node.x - width / 2; const contentTop = node.swimlaneContentTop !== void 0 ? node.swimlaneContentTop : laneTop + height / 3; const titlePaddingY = isLR ? 4 : 0; const desiredTitleSize = bbox.height + 2 * titlePaddingY; let titleRect; let bodyRect; if (isLR) { const titleWidth = Math.max(desiredTitleSize, bbox.height + 2 * titlePaddingY); const bodyX = laneLeft + titleWidth; const bodyWidth = Math.max(0, width - titleWidth); if (node.look === "handDrawn") { const rc = at.svg(shapeSvg); const titleOptions = userNodeOverrides(node, { roughness: 0.7, fill: clusterBkg, stroke: laneStroke, fillWeight: 3, seed: handDrawnSeed }); const bodyOptions = userNodeOverrides(node, { roughness: 0.7, fill: "none", stroke: laneStroke, seed: handDrawnSeed }); const roughTitle = rc.rectangle(laneLeft, laneTop, titleWidth, height, titleOptions); titleRect = shapeSvg.insert(() => roughTitle, ":first-child"); const roughBody = rc.rectangle(bodyX, laneTop, bodyWidth, height, bodyOptions); bodyRect = shapeSvg.insert(() => roughBody, ":first-child"); titleRect.select("path:nth-child(2)").attr("style", borderStyles.join(";")); titleRect.select("path").attr("style", backgroundStyles.join(";").replace("fill", "stroke")); } else { titleRect = shapeSvg.insert("rect", ":first-child"); bodyRect = shapeSvg.insert("rect", ":first-child"); titleRect.attr("class", "swimlane-title").attr("style", nodeStyles).attr("x", laneLeft).attr("y", laneTop).attr("width", titleWidth).attr("height", height).attr("fill", clusterBkg).attr("stroke", laneStroke); bodyRect.attr("class", "swimlane-body").attr("style", nodeStyles).attr("x", bodyX).attr("y", laneTop).attr("width", bodyWidth).attr("height", height).attr("fill", "none").attr("stroke", laneStroke); } const labelCenterX = laneLeft + titleWidth / 2; const labelCenterY = node.y; labelEl.attr( "transform", `translate(${labelCenterX}, ${labelCenterY}) rotate(-90) translate(${-bbox.width / 2}, ${-bbox.height / 2})` ); } else { const headerMaxHeight = Math.max(0, contentTop - laneTop); const titleHeight = Math.min(desiredTitleSize, headerMaxHeight); const bodyY = laneTop + titleHeight; const contentHeight = Math.max(0, laneBottom - bodyY); const x = node.x - width / 2; if (node.look === "handDrawn") { const rc = at.svg(shapeSvg); const titleOptions = userNodeOverrides(node, { roughness: 0.7, fill: clusterBkg, stroke: laneStroke, fillWeight: 3, seed: handDrawnSeed }); const bodyOptions = userNodeOverrides(node, { roughness: 0.7, fill: "none", stroke: laneStroke, seed: handDrawnSeed }); const roughTitle = rc.rectangle(x, laneTop, width, titleHeight, titleOptions); titleRect = shapeSvg.insert(() => roughTitle, ":first-child"); const roughBody = rc.rectangle(x, bodyY, width, contentHeight, bodyOptions); bodyRect = shapeSvg.insert(() => roughBody, ":first-child"); titleRect.select("path:nth-child(2)").attr("style", borderStyles.join(";")); titleRect.select("path").attr("style", backgroundStyles.join(";").replace("fill", "stroke")); } else { titleRect = shapeSvg.insert("rect", ":first-child"); bodyRect = shapeSvg.insert("rect", ":first-child"); titleRect.attr("class", "swimlane-title").attr("style", nodeStyles).attr("x", x).attr("y", laneTop).attr("width", width).attr("height", titleHeight).attr("fill", clusterBkg).attr("stroke", laneStroke); bodyRect.attr("class", "swimlane-body").attr("style", nodeStyles).attr("x", x).attr("y", bodyY).attr("width", width).attr("height", contentHeight).attr("fill", "none").attr("stroke", laneStroke); } const labelX = node.x - bbox.width / 2; const labelY = laneTop + (titleHeight - bbox.height) / 2; labelEl.attr("transform", `translate(${labelX}, ${labelY})`); } log.trace("Swimlane data ", node, JSON.stringify(node)); if (labelStyles) { const span = labelEl.select("span"); if (span) { span.attr("style", labelStyles); } } node.offsetX = 0; node.width = width; node.height = height; node.offsetY = bbox.height - padding / 2; node.intersect = function(point) { return intersect_rect_default(node, point); }; return { cluster: shapeSvg, labelBBox: bbox }; }, "swimlane"); // src/rendering-util/rendering-elements/clusters.js var rect = /* @__PURE__ */ __name(async (parent, node) => { log.info("Creating subgraph rect for ", node.id, node); const siteConfig = getConfig2(); const { themeVariables, handDrawnSeed } = siteConfig; const { clusterBkg, clusterBorder } = themeVariables; const { labelStyles, nodeStyles, borderStyles, backgroundStyles } = styles2String(node); const shapeSvg = parent.insert("g").attr("class", "cluster " + node.cssClasses).attr("id", node.domId).attr("data-look", node.look); const useHtmlLabels = getEffectiveHtmlLabels(siteConfig); const labelEl = shapeSvg.insert("g").attr("class", "cluster-label "); let text2; if (node.labelType === "markdown") { text2 = await createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels, isNode: true, width: node.width }); } else { text2 = await createLabel_default(labelEl, node.label, node.labelStyle || "", false, true); } let bbox = text2.getBBox(); if (getEffectiveHtmlLabels(siteConfig)) { const div = text2.children[0]; const dv = select_default(text2); bbox = div.getBoundingClientRect(); dv.attr("width", bbox.width); dv.attr("height", bbox.height); } const width = node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width; if (node.width <= bbox.width + node.padding) { node.diff = (width - node.width) / 2 - node.padding; } else { node.diff = -node.padding; } const height = node.height; const x = node.x - width / 2; const y = node.y - height / 2; log.trace("Data ", node, JSON.stringify(node)); let rect2; if (node.look === "handDrawn") { const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, { roughness: 0.7, fill: clusterBkg, // fill: 'red', stroke: clusterBorder, fillWeight: 3, seed: handDrawnSeed }); const roughNode = rc.path(createRoundedRectPathD(x, y, width, height, 0), options); rect2 = shapeSvg.insert(() => { log.debug("Rough node insert CXC", roughNode); return roughNode; }, ":first-child"); rect2.select("path:nth-child(2)").attr("style", borderStyles.join(";")); rect2.select("path").attr("style", backgroundStyles.join(";").replace("fill", "stroke")); } else { rect2 = shapeSvg.insert("rect", ":first-child"); rect2.attr("style", nodeStyles).attr("rx", node.rx).attr("ry", node.ry).attr("x", x).attr("y", y).attr("width", width).attr("height", height); } const { subGraphTitleTopMargin } = getSubGraphTitleMargins(siteConfig); labelEl.attr( "transform", // This puts the label on top of the box instead of inside it `translate(${node.x - bbox.width / 2}, ${node.y - node.height / 2 + subGraphTitleTopMargin})` ); if (labelStyles) { const span = labelEl.select("span"); if (span) { span.attr("style", labelStyles); } } const rectBox = rect2.node().getBBox(); node.offsetX = 0; node.width = rectBox.width; node.height = rectBox.height; node.offsetY = bbox.height - node.padding / 2; node.intersect = function(point) { return intersect_rect_default(node, point); }; return { cluster: shapeSvg, labelBBox: bbox }; }, "rect"); var noteGroup = /* @__PURE__ */ __name((parent, node) => { const shapeSvg = parent.insert("g").attr("class", "note-cluster").attr("id", node.domId); const rect2 = shapeSvg.insert("rect", ":first-child"); const padding = 0 * node.padding; const halfPadding = padding / 2; rect2.attr("rx", node.rx).attr("ry", node.ry).attr("x", node.x - node.width / 2 - halfPadding).attr("y", node.y - node.height / 2 - halfPadding).attr("width", node.width + padding).attr("height", node.height + padding).attr("fill", "none"); const rectBox = rect2.node().getBBox(); node.width = rectBox.width; node.height = rectBox.height; node.intersect = function(point) { return intersect_rect_default(node, point); }; return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } }; }, "noteGroup"); var roundedWithTitle = /* @__PURE__ */ __name(async (parent, node) => { const siteConfig = getConfig2(); const { themeVariables, handDrawnSeed } = siteConfig; const { altBackground, compositeBackground, compositeTitleBackground, nodeBorder } = themeVariables; const shapeSvg = parent.insert("g").attr("class", node.cssClasses).attr("id", node.domId).attr("data-id", node.id).attr("data-look", node.look); const outerRectG = shapeSvg.insert("g", ":first-child"); const label = shapeSvg.insert("g").attr("class", "cluster-label"); let innerRect = shapeSvg.append("rect"); const text2 = await createLabel_default(label, node.label, node.labelStyle, void 0, true); let bbox = text2.getBBox(); if (getEffectiveHtmlLabels(siteConfig)) { const div = text2.children[0]; const dv = select_default(text2); bbox = div.getBoundingClientRect(); dv.attr("width", bbox.width); dv.attr("height", bbox.height); } const padding = 0 * node.padding; const halfPadding = padding / 2; const width = (node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width) + padding; if (node.width <= bbox.width + node.padding) { node.diff = (width - node.width) / 2 - node.padding; } else { node.diff = -node.padding; } const height = node.height + padding; const innerHeight = node.height + padding - bbox.height - 6; const x = node.x - width / 2; const y = node.y - height / 2; node.width = width; const innerY = node.y - node.height / 2 - halfPadding + bbox.height + 2; let rect2; if (node.look === "handDrawn") { const isAlt = node.cssClasses.includes("statediagram-cluster-alt"); const rc = at.svg(shapeSvg); const roughOuterNode = node.rx || node.ry ? rc.path(createRoundedRectPathD(x, y, width, height, 10), { roughness: 0.7, fill: compositeTitleBackground, fillStyle: "solid", stroke: nodeBorder, seed: handDrawnSeed }) : rc.rectangle(x, y, width, height, { seed: handDrawnSeed }); rect2 = shapeSvg.insert(() => roughOuterNode, ":first-child"); const roughInnerNode = rc.rectangle(x, innerY, width, innerHeight, { fill: isAlt ? altBackground : compositeBackground, fillStyle: isAlt ? "hachure" : "solid", stroke: nodeBorder, seed: handDrawnSeed }); rect2 = shapeSvg.insert(() => roughOuterNode, ":first-child"); innerRect = shapeSvg.insert(() => roughInnerNode); } else { rect2 = outerRectG.insert("rect", ":first-child"); const outerRectClass = "outer"; rect2.attr("class", outerRectClass).attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("data-look", node.look); innerRect.attr("class", "inner").attr("x", x).attr("y", innerY).attr("width", width).attr("height", innerHeight); } label.attr( "transform", `translate(${node.x - bbox.width / 2}, ${y + 1 - (getEffectiveHtmlLabels(siteConfig) ? 0 : 3)})` ); const rectBox = rect2.node().getBBox(); node.height = rectBox.height; node.offsetX = 0; node.offsetY = bbox.height - node.padding / 2; node.labelBBox = bbox; node.intersect = function(point) { return intersect_rect_default(node, point); }; return { cluster: shapeSvg, labelBBox: bbox }; }, "roundedWithTitle"); var kanbanSection = /* @__PURE__ */ __name(async (parent, node) => { log.info("Creating subgraph rect for ", node.id, node); const siteConfig = getConfig2(); const { themeVariables, handDrawnSeed } = siteConfig; const { clusterBkg, clusterBorder } = themeVariables; const { labelStyles, nodeStyles, borderStyles, backgroundStyles } = styles2String(node); const shapeSvg = parent.insert("g").attr("class", "cluster " + node.cssClasses).attr("id", node.domId).attr("data-look", node.look); const useHtmlLabels = getEffectiveHtmlLabels(siteConfig); const labelEl = shapeSvg.insert("g").attr("class", "cluster-label "); const text2 = await createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels, isNode: true, width: node.width }); let bbox = text2.getBBox(); if (getEffectiveHtmlLabels(siteConfig)) { const div = text2.children[0]; const dv = select_default(text2); bbox = div.getBoundingClientRect(); dv.attr("width", bbox.width); dv.attr("height", bbox.height); } const width = node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width; if (node.width <= bbox.width + node.padding) { node.diff = (width - node.width) / 2 - node.padding; } else { node.diff = -node.padding; } const height = node.height; const x = node.x - width / 2; const y = node.y - height / 2; log.trace("Data ", node, JSON.stringify(node)); let rect2; if (node.look === "handDrawn") { const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, { roughness: 0.7, fill: clusterBkg, // fill: 'red', stroke: clusterBorder, fillWeight: 4, seed: handDrawnSeed }); const roughNode = rc.path(createRoundedRectPathD(x, y, width, height, node.rx), options); rect2 = shapeSvg.insert(() => { log.debug("Rough node insert CXC", roughNode); return roughNode; }, ":first-child"); rect2.select("path:nth-child(2)").attr("style", borderStyles.join(";")); rect2.select("path").attr("style", backgroundStyles.join(";").replace("fill", "stroke")); } else { rect2 = shapeSvg.insert("rect", ":first-child"); rect2.attr("style", nodeStyles).attr("rx", node.rx).attr("ry", node.ry).attr("x", x).attr("y", y).attr("width", width).attr("height", height); } const { subGraphTitleTopMargin } = getSubGraphTitleMargins(siteConfig); labelEl.attr( "transform", // This puts the label on top of the box instead of inside it `translate(${node.x - bbox.width / 2}, ${node.y - node.height / 2 + subGraphTitleTopMargin})` ); if (labelStyles) { const span = labelEl.select("span"); if (span) { span.attr("style", labelStyles); } } const rectBox = rect2.node().getBBox(); node.offsetX = 0; node.width = rectBox.width; node.height = rectBox.height; node.offsetY = bbox.height - node.padding / 2; node.intersect = function(point) { return intersect_rect_default(node, point); }; return { cluster: shapeSvg, labelBBox: bbox }; }, "kanbanSection"); var divider = /* @__PURE__ */ __name((parent, node) => { const siteConfig = getConfig2(); const { themeVariables, handDrawnSeed } = siteConfig; const { nodeBorder } = themeVariables; const shapeSvg = parent.insert("g").attr("class", node.cssClasses).attr("id", node.domId).attr("data-look", node.look); const outerRectG = shapeSvg.insert("g", ":first-child"); const padding = 0 * node.padding; const width = node.width + padding; node.diff = -node.padding; const height = node.height + padding; const x = node.x - width / 2; const y = node.y - height / 2; node.width = width; let rect2; if (node.look === "handDrawn") { const rc = at.svg(shapeSvg); const roughOuterNode = rc.rectangle(x, y, width, height, { fill: "lightgrey", roughness: 0.5, strokeLineDash: [5], stroke: nodeBorder, seed: handDrawnSeed }); rect2 = shapeSvg.insert(() => roughOuterNode, ":first-child"); } else { rect2 = outerRectG.insert("rect", ":first-child"); let outerRectClass = "outer"; if (node.look === "neo") { outerRectClass = "divider"; } else { outerRectClass = "divider"; } rect2.attr("class", outerRectClass).attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("data-look", node.look); } const rectBox = rect2.node().getBBox(); node.height = rectBox.height; node.offsetX = 0; node.offsetY = 0; node.intersect = function(point) { return intersect_rect_default(node, point); }; return { cluster: shapeSvg, labelBBox: {} }; }, "divider"); var squareRect = rect; var shapes = { rect, squareRect, roundedWithTitle, noteGroup, divider, kanbanSection, swimlane }; var clusterElems = /* @__PURE__ */ new Map(); var insertCluster = /* @__PURE__ */ __name(async (elem, node) => { const shape = node.shape || "rect"; const cluster = await shapes[shape](elem, node); clusterElems.set(node.id, cluster); return cluster; }, "insertCluster"); var clear = /* @__PURE__ */ __name(() => { clusterElems = /* @__PURE__ */ new Map(); }, "clear"); // src/rendering-util/rendering-elements/intersect/intersect-node.js function intersectNode(node, point) { return node.intersect(point); } __name(intersectNode, "intersectNode"); var intersect_node_default = intersectNode; // src/rendering-util/rendering-elements/intersect/intersect-ellipse.js function intersectEllipse(node, rx, ry, point) { var cx = node.x; var cy = node.y; var px = cx - point.x; var py = cy - point.y; var det = Math.sqrt(rx * rx * py * py + ry * ry * px * px); var dx = Math.abs(rx * ry * px / det); if (point.x < cx) { dx = -dx; } var dy = Math.abs(rx * ry * py / det); if (point.y < cy) { dy = -dy; } return { x: cx + dx, y: cy + dy }; } __name(intersectEllipse, "intersectEllipse"); var intersect_ellipse_default = intersectEllipse; // src/rendering-util/rendering-elements/intersect/intersect-circle.js function intersectCircle(node, rx, point) { return intersect_ellipse_default(node, rx, rx, point); } __name(intersectCircle, "intersectCircle"); var intersect_circle_default = intersectCircle; // src/rendering-util/rendering-elements/intersect/intersect-line.js function intersectLine(p1, p2, q1, q2) { { const a1 = p2.y - p1.y; const b1 = p1.x - p2.x; const c1 = p2.x * p1.y - p1.x * p2.y; const r3 = a1 * q1.x + b1 * q1.y + c1; const r4 = a1 * q2.x + b1 * q2.y + c1; const epsilon = 1e-6; if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { return; } const a2 = q2.y - q1.y; const b2 = q1.x - q2.x; const c2 = q2.x * q1.y - q1.x * q2.y; const r1 = a2 * p1.x + b2 * p1.y + c2; const r2 = a2 * p2.x + b2 * p2.y + c2; if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) { return; } const denom = a1 * b2 - a2 * b1; if (denom === 0) { return; } const offset = Math.abs(denom / 2); let num = b1 * c2 - b2 * c1; const x = num < 0 ? (num - offset) / denom : (num + offset) / denom; num = a2 * c1 - a1 * c2; const y = num < 0 ? (num - offset) / denom : (num + offset) / denom; return { x, y }; } } __name(intersectLine, "intersectLine"); function sameSign(r1, r2) { return r1 * r2 > 0; } __name(sameSign, "sameSign"); var intersect_line_default = intersectLine; // src/rendering-util/rendering-elements/intersect/intersect-polygon.js function intersectPolygon(node, polyPoints, point) { let x1 = node.x; let y1 = node.y; let intersections = []; let minX = Number.POSITIVE_INFINITY; let minY = Number.POSITIVE_INFINITY; if (typeof polyPoints.forEach === "function") { polyPoints.forEach(function(entry) { minX = Math.min(minX, entry.x); minY = Math.min(minY, entry.y); }); } else { minX = Math.min(minX, polyPoints.x); minY = Math.min(minY, polyPoints.y); } let left = x1 - node.width / 2 - minX; let top = y1 - node.height / 2 - minY; for (let i = 0; i < polyPoints.length; i++) { let p1 = polyPoints[i]; let p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0]; let intersect = intersect_line_default( node, point, { x: left + p1.x, y: top + p1.y }, { x: left + p2.x, y: top + p2.y } ); if (intersect) { intersections.push(intersect); } } if (!intersections.length) { return node; } if (intersections.length > 1) { intersections.sort(function(p, q) { let pdx = p.x - point.x; let pdy = p.y - point.y; let distp = Math.sqrt(pdx * pdx + pdy * pdy); let qdx = q.x - point.x; let qdy = q.y - point.y; let distq = Math.sqrt(qdx * qdx + qdy * qdy); return distp < distq ? -1 : distp === distq ? 0 : 1; }); } return intersections[0]; } __name(intersectPolygon, "intersectPolygon"); var intersect_polygon_default = intersectPolygon; // src/rendering-util/rendering-elements/intersect/index.js var intersect_default = { node: intersect_node_default, circle: intersect_circle_default, ellipse: intersect_ellipse_default, polygon: intersect_polygon_default, rect: intersect_rect_default }; // src/rendering-util/rendering-elements/shapes/anchor.ts function anchor(parent, node) { const { labelStyles } = styles2String(node); node.labelStyle = labelStyles; const classes = getNodeClasses(node); let cssClasses = classes; if (!classes) { cssClasses = "anchor"; } const shapeSvg = parent.insert("g").attr("class", cssClasses).attr("id", node.domId || node.id); const radius = 1; const { cssStyles } = node; const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, { fill: "black", stroke: "none", fillStyle: "solid" }); if (node.look !== "handDrawn") { options.roughness = 0; } const roughNode = rc.circle(0, 0, radius * 2, options); const circleElem = shapeSvg.insert(() => roughNode, ":first-child"); circleElem.attr("class", "anchor").attr("style", handleUndefinedAttr(cssStyles)); updateNodeBounds(node, circleElem); node.intersect = function(point) { log.info("Circle intersect", node, radius, point); return intersect_default.circle(node, radius, point); }; return shapeSvg; } __name(anchor, "anchor"); // src/rendering-util/rendering-elements/shapes/bowTieRect.ts function generateArcPoints(x1, y1, x2, y2, rx, ry, clockwise) { const numPoints = 20; const midX = (x1 + x2) / 2; const midY = (y1 + y2) / 2; const angle = Math.atan2(y2 - y1, x2 - x1); const dx = (x2 - x1) / 2; const dy = (y2 - y1) / 2; const transformedX = dx / rx; const transformedY = dy / ry; const distance = Math.sqrt(transformedX ** 2 + transformedY ** 2); if (distance > 1) { throw new Error("The given radii are too small to create an arc between the points."); } const scaledCenterDistance = Math.sqrt(1 - distance ** 2); const centerX = midX + scaledCenterDistance * ry * Math.sin(angle) * (clockwise ? -1 : 1); const centerY = midY - scaledCenterDistance * rx * Math.cos(angle) * (clockwise ? -1 : 1); const startAngle = Math.atan2((y1 - centerY) / ry, (x1 - centerX) / rx); const endAngle = Math.atan2((y2 - centerY) / ry, (x2 - centerX) / rx); let angleRange = endAngle - startAngle; if (clockwise && angleRange < 0) { angleRange += 2 * Math.PI; } if (!clockwise && angleRange > 0) { angleRange -= 2 * Math.PI; } const points = []; for (let i = 0; i < numPoints; i++) { const t = i / (numPoints - 1); const angle2 = startAngle + t * angleRange; const x = centerX + rx * Math.cos(angle2); const y = centerY + ry * Math.sin(angle2); points.push({ x, y }); } return points; } __name(generateArcPoints, "generateArcPoints"); function calculateArcSagitta(chord, radiusX, radiusY) { const [semiMajorAxis, semiMinorAxis] = [radiusX, radiusY].sort((a, b) => b - a); return semiMinorAxis * (1 - Math.sqrt(1 - (chord / semiMajorAxis / 2) ** 2)); } __name(calculateArcSagitta, "calculateArcSagitta"); async function bowTieRect(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; const labelPaddingX = node.look === "neo" ? 16 : nodePadding; const labelPaddingY = node.look === "neo" ? 12 : nodePadding; const calcTotalHeight = /* @__PURE__ */ __name((labelHeight) => labelHeight + labelPaddingY, "calcTotalHeight"); const calcEllipseRadius = /* @__PURE__ */ __name((totalHeight2) => { const ry2 = totalHeight2 / 2; const rx2 = ry2 / (2.5 + totalHeight2 / 50); return [rx2, ry2]; }, "calcEllipseRadius"); const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); const totalHeight = calcTotalHeight(node?.height ? node?.height : bbox.height); const [rx, ry] = calcEllipseRadius(totalHeight); const sagitta = calculateArcSagitta(totalHeight, rx, ry); const totalWidth = (node?.width ? node?.width : bbox.width) + labelPaddingX * 2 + sagitta; const w = totalWidth - sagitta; const h = totalHeight; const { cssStyles } = node; const points = [ { x: w / 2, y: -h / 2 }, { x: -w / 2, y: -h / 2 }, ...generateArcPoints(-w / 2, -h / 2, -w / 2, h / 2, rx, ry, false), { x: w / 2, y: h / 2 }, ...generateArcPoints(w / 2, h / 2, w / 2, -h / 2, rx, ry, true) ]; const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, {}); if (node.look !== "handDrawn") { options.roughness = 0; options.fillStyle = "solid"; } const bowTieRectPath = createPathFromPoints(points); const bowTieRectShapePath = rc.path(bowTieRectPath, options); const bowTieRectShape = shapeSvg.insert(() => bowTieRectShapePath, ":first-child"); bowTieRectShape.attr("class", "basic label-container outer-path"); if (cssStyles && node.look !== "handDrawn") { bowTieRectShape.selectAll("path").attr("style", cssStyles); } if (nodeStyles && node.look !== "handDrawn") { bowTieRectShape.selectAll("path").attr("style", nodeStyles); } bowTieRectShape.attr("transform", `translate(${rx / 2}, 0)`); updateNodeBounds(node, bowTieRectShape); node.intersect = function(point) { const pos = intersect_default.polygon(node, points, point); return pos; }; return shapeSvg; } __name(bowTieRect, "bowTieRect"); // src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts function insertPolygonShape(parent, w, h, points) { return parent.insert("polygon", ":first-child").attr( "points", points.map(function(d) { return d.x + "," + d.y; }).join(" ") ).attr("class", "label-container").attr("transform", "translate(" + -w / 2 + "," + h / 2 + ")"); } __name(insertPolygonShape, "insertPolygonShape"); // src/rendering-util/rendering-elements/shapes/card.ts var NOTCH_SIZE = 12; async function card(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; const labelPaddingX = node.look === "neo" ? 28 : nodePadding; const labelPaddingY = node.look === "neo" ? 24 : nodePadding; const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); const w = (node?.width ?? bbox.width) + (node.look === "neo" ? labelPaddingX * 2 : labelPaddingX + NOTCH_SIZE); const h = (node?.height ?? bbox.height) + (node.look === "neo" ? labelPaddingY * 2 : labelPaddingY); const left = 0; const right = w; const top = -h; const bottom = 0; const points = [ { x: left + NOTCH_SIZE, y: top }, { x: right, y: top }, { x: right, y: bottom }, { x: left, y: bottom }, { x: left, y: top + NOTCH_SIZE }, { x: left + NOTCH_SIZE, y: top } ]; let polygon; const { cssStyles } = node; if (node.look === "handDrawn") { const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createPathFromPoints(points); const roughNode = rc.path(pathData, options); polygon = shapeSvg.insert(() => roughNode, ":first-child").attr("transform", `translate(${-w / 2}, ${h / 2})`); if (cssStyles) { polygon.attr("style", cssStyles); } } else { polygon = insertPolygonShape(shapeSvg, w, h, points); } if (nodeStyles) { polygon.attr("style", nodeStyles); } updateNodeBounds(node, polygon); node.intersect = function(point) { return intersect_default.polygon(node, points, point); }; return shapeSvg; } __name(card, "card"); // src/rendering-util/rendering-elements/shapes/choice.ts function choice(parent, node) { const { nodeStyles } = styles2String(node); node.label = ""; const shapeSvg = parent.insert("g").attr("class", getNodeClasses(node)).attr("id", node.domId ?? node.id); const { cssStyles } = node; const s = Math.max(28, node.width ?? 0); const points = [ { x: 0, y: s / 2 }, { x: s / 2, y: 0 }, { x: 0, y: -s / 2 }, { x: -s / 2, y: 0 } ]; const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, {}); if (node.look !== "handDrawn") { options.roughness = 0; options.fillStyle = "solid"; } const choicePath = createPathFromPoints(points); const roughNode = rc.path(choicePath, options); const choiceShape = shapeSvg.insert(() => roughNode, ":first-child"); if (cssStyles && node.look !== "handDrawn") { choiceShape.selectAll("path").attr("style", cssStyles); } if (nodeStyles && node.look !== "handDrawn") { choiceShape.selectAll("path").attr("style", nodeStyles); } node.width = 28; node.height = 28; node.intersect = function(point) { return intersect_default.polygon(node, points, point); }; return shapeSvg; } __name(choice, "choice"); // src/rendering-util/rendering-elements/shapes/circle.ts async function circle(parent, node, options) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node)); const labelPadding = 16; const padding = options?.padding ?? halfPadding; const radius = node.look === "neo" ? bbox.width / 2 + labelPadding * 2 : bbox.width / 2 + padding; let circleElem; const { cssStyles } = node; if (node.look === "handDrawn") { const rc = at.svg(shapeSvg); const options2 = userNodeOverrides(node, {}); const roughNode = rc.circle(0, 0, radius * 2, options2); circleElem = shapeSvg.insert(() => roughNode, ":first-child"); circleElem.attr("class", "basic label-container").attr("style", handleUndefinedAttr(cssStyles)); } else { circleElem = shapeSvg.insert("circle", ":first-child").attr("class", "basic label-container").attr("style", nodeStyles).attr("r", radius).attr("cx", 0).attr("cy", 0); } updateNodeBounds(node, circleElem); node.calcIntersect = function(bounds, point) { const radius2 = bounds.width / 2; return intersect_default.circle(bounds, radius2, point); }; node.intersect = function(point) { log.info("Circle intersect", node, radius, point); return intersect_default.circle(node, radius, point); }; return shapeSvg; } __name(circle, "circle"); // src/rendering-util/rendering-elements/shapes/crossedCircle.ts function createLine(r) { const xAxis45 = Math.cos(Math.PI / 4); const yAxis45 = Math.sin(Math.PI / 4); const lineLength = r * 2; const pointQ1 = { x: lineLength / 2 * xAxis45, y: lineLength / 2 * yAxis45 }; const pointQ2 = { x: -(lineLength / 2) * xAxis45, y: lineLength / 2 * yAxis45 }; const pointQ3 = { x: -(lineLength / 2) * xAxis45, y: -(lineLength / 2) * yAxis45 }; const pointQ4 = { x: lineLength / 2 * xAxis45, y: -(lineLength / 2) * yAxis45 }; return `M ${pointQ2.x},${pointQ2.y} L ${pointQ4.x},${pointQ4.y} M ${pointQ1.x},${pointQ1.y} L ${pointQ3.x},${pointQ3.y}`; } __name(createLine, "createLine"); function crossedCircle(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; node.label = ""; const shapeSvg = parent.insert("g").attr("class", getNodeClasses(node)).attr("id", node.domId ?? node.id); const radius = Math.max(30, node?.width ?? 0); const { cssStyles } = node; const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, {}); if (node.look !== "handDrawn") { options.roughness = 0; options.fillStyle = "solid"; } const circleNode = rc.circle(0, 0, radius * 2, options); const linePath = createLine(radius); const lineNode = rc.path(linePath, options); const crossedCircle2 = shapeSvg.insert(() => circleNode, ":first-child"); crossedCircle2.insert(() => lineNode); crossedCircle2.attr("class", "outer-path"); if (cssStyles && node.look !== "handDrawn") { crossedCircle2.selectAll("path").attr("style", cssStyles); } if (nodeStyles && node.look !== "handDrawn") { crossedCircle2.selectAll("path").attr("style", nodeStyles); } updateNodeBounds(node, crossedCircle2); node.intersect = function(point) { log.info("crossedCircle intersect", node, { radius, point }); const pos = intersect_default.circle(node, radius, point); return pos; }; return shapeSvg; } __name(crossedCircle, "crossedCircle"); // src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts function generateCirclePoints2(centerX, centerY, radius, numPoints = 100, startAngle = 0, endAngle = 180) { const points = []; const startAngleRad = startAngle * Math.PI / 180; const endAngleRad = endAngle * Math.PI / 180; const angleRange = endAngleRad - startAngleRad; const angleStep = angleRange / (numPoints - 1); for (let i = 0; i < numPoints; i++) { const angle = startAngleRad + i * angleStep; const x = centerX + radius * Math.cos(angle); const y = centerY + radius * Math.sin(angle); points.push({ x: -x, y: -y }); } return points; } __name(generateCirclePoints2, "generateCirclePoints"); async function curlyBraceLeft(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); const paddingX = node.look === "neo" ? 18 : node.padding ?? 0; const paddingY = node.look === "neo" ? 12 : node.padding ?? 0; const w = bbox.width + paddingX; const h = bbox.height + paddingY; const radius = Math.max(5, h * 0.1); const { cssStyles } = node; const points = [ ...generateCirclePoints2(w / 2, -h / 2, radius, 30, -90, 0), { x: -w / 2 - radius, y: radius }, ...generateCirclePoints2(w / 2 + radius * 2, -radius, radius, 20, -180, -270), ...generateCirclePoints2(w / 2 + radius * 2, radius, radius, 20, -90, -180), { x: -w / 2 - radius, y: -h / 2 }, ...generateCirclePoints2(w / 2, h / 2, radius, 20, 0, 90) ]; const rectPoints = [ { x: w / 2, y: -h / 2 - radius }, { x: -w / 2, y: -h / 2 - radius }, ...generateCirclePoints2(w / 2, -h / 2, radius, 20, -90, 0), { x: -w / 2 - radius, y: -radius }, ...generateCirclePoints2(w / 2 + w * 0.1, -radius, radius, 20, -180, -270), ...generateCirclePoints2(w / 2 + w * 0.1, radius, radius, 20, -90, -180), { x: -w / 2 - radius, y: h / 2 }, ...generateCirclePoints2(w / 2, h / 2, radius, 20, 0, 90), { x: -w / 2, y: h / 2 + radius }, { x: w / 2, y: h / 2 + radius } ]; const rc = at.svg(shapeSvg); const options = userNodeOverrides(node, { fill: "none" }); if (node.look !== "handDrawn") { options.roughness = 0; options.fillStyle = "solid"; } const curlyBraceLeftPath = createPathFromPoints(points); const newCurlyBracePath = curlyBraceLeftPath.replace("Z", ""); const curlyBraceLeftNode = rc.path(newCurlyBracePath, options); const rectPath = createPathFromPoints(rectPoints); const rectShape = rc.path(rectPath, { ...options }); const curlyBraceLeftShape = shapeSvg.insert("g", ":first-child"); curlyBraceLeftShape.insert(() => rectShape, ":first-child").attr("stroke-opacity", 0); curlyBraceLeftShape.insert(() => curlyBraceLeftNode, ":first-child"); curlyBraceLeftShape.attr("class", "text"); if (cssStyles && node.look !== "handDrawn") { curlyBraceLeftShape.selectAll("path").attr("style", cssStyles); } if (nodeStyles && node.look !== "handDrawn") { curlyBraceLeftShape.selectAll("path").attr("style", nodeStyles); } curlyBraceLeftShape.attr("transform", `translate(${radius}, 0)`); label.attr( "transform", `translate(${-w / 2 + radius - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` ); updateNodeBounds(node, curlyBraceLeftShape); node.intersect = function(point) { const pos = intersect_default.polygon(node, rectPoints, point); return pos; }; return shapeSvg; } __name(curlyBraceLeft, "curlyBraceLeft"); // src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts function generateCirclePoints3(centerX, centerY, radius, numPoints = 100, startAngle = 0, endAngle = 180) { const points = []; const startAngleRad = startAngle * Math.PI / 180; const endAngleRad = endAngle * Math.PI / 180; const angleRange = endAngleRad - startAngleRad; const angleStep = angleRange / (numPoints - 1); for (let i = 0; i < numPoints; i++) { const angle = startAngleRad + i * angleStep; const x = centerX + radius * Math.cos(angle); const y = centerY + radius * Math.sin(angle); points.push({ x, y }); } return points; } __name(generateCirclePoints3, "generateCirclePoints"); async function curlyBraceRight(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); const labelPaddingX = node.look === "neo" ? 18 : node.padding ?? 0; const labelPaddingY = node.look === "neo" ? 12 : node.padding ?? 0; const w = bbox.width + (node.look === "neo" ? labelPaddingX * 2 : labelPaddingX); const h = bbox.height + (node.look === "neo" ? labelPaddingY * 2 : labelPaddingY); const radius = Math.max(5, h * 0.1); const { cssStyles } = node; const points = [ ...generateCirclePoints3(w / 2, -h / 2, radius, 20, -90, 0), { x: w / 2 + radius, y: -radius }, ...generateCirclePoints3(w / 2 + radius * 2, -radius, radius, 20, -180, -270), ...generateCirclePoints3(w / 2 + radius * 2, radius, radius, 20, -90, -180), { x: w / 2 + radius, y: h / 2 }, ...generateCirclePoints3(w / 2, h / 2, radius, 20, 0, 90) ]; const rectPoints = [ { x: -w / 2, y: -h / 2 - radius }, { x: w / 2, y: -h / 2 - radius }, ...generateCirclePoints3(w / 2, -h / 2, radius, 20, -90, 0), { x: w / 2 + radius, y: -radius }, ...generateCirclePoints3(w / 2 + radius * 2, -radius, radius, 20, -180, -270), ...generateCirclePoints3(w / 2 + radius * 2, radius, radi