UNPKG

mermaid

Version:

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

1,314 lines (1,291 loc) 190 kB
import { getSubGraphTitleMargins } from "./chunk-AC5SNWB5.mjs"; import { compileStyles, solidStateFill, styles2String, userNodeOverrides } from "./chunk-UWXLY5YG.mjs"; import { createText, getIconSVG } from "./chunk-QESNASVV.mjs"; import { calculateTextWidth, decodeEntities, handleUndefinedAttr, parseFontSize } from "./chunk-55PJQP7W.mjs"; import { __name, common_default, defaultConfig_default, evaluate, getConfig, getConfig2, hasKatex, log, parseGenericTypes, renderKatex, sanitizeText, sanitizeText2 } from "./chunk-3XYRH5AP.mjs"; // src/rendering-util/rendering-elements/shapes/util.ts import { select } from "d3"; 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 text2 = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig2()), { useHtmlLabels, width: node.width || getConfig2().flowchart?.wrappingWidth, // @ts-expect-error -- This is currently not used. Should this be `classes` instead? cssClasses: "markdown-node-label", style: node.labelStyle, addSvgBackground: !!node.icon || !!node.img }); let bbox = text2.getBBox(); const halfPadding = (node?.padding ?? 0) / 2; if (useHtmlLabels) { const div = text2.children[0]; const dv = select(text2); const images = div.getElementsByTagName("img"); if (images) { const noImgText = label.replace(/<img[^>]*>/g, "").trim() === ""; await Promise.all( [...images].map( (img) => new Promise((res) => { function setupImage() { img.style.display = "flex"; img.style.flexDirection = "column"; if (noImgText) { const bodyFontSize = getConfig2().fontSize ? getConfig2().fontSize : window.getComputedStyle(document.body).fontSize; const enlargingFactor = 5; const [parsedBodyFontSize = defaultConfig_default.fontSize] = parseFontSize(bodyFontSize); const width = parsedBodyFontSize * enlargingFactor + "px"; img.style.minWidth = width; img.style.maxWidth = width; } else { img.style.width = "100%"; } res(img); } __name(setupImage, "setupImage"); setTimeout(() => { if (img.complete) { setupImage(); } }); img.addEventListener("error", setupImage); img.addEventListener("load", setupImage); }) ) ); } 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 || evaluate(getConfig2()?.flowchart?.htmlLabels); 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 (evaluate(getConfig2()?.flowchart?.htmlLabels)) { const div = text2.children[0]; const dv = select(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"); // src/rendering-util/rendering-elements/clusters.js import { select as select3 } from "d3"; import rough from "roughjs"; // 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 import { select as select2 } from "d3"; function applyStyle(dom, styleFn) { if (styleFn) { dom.attr("style", styleFn); } } __name(applyStyle, "applyStyle"); async function addHtmlLabel(node) { const fo = select2(document.createElementNS("http://www.w3.org/2000/svg", "foreignObject")); const div = fo.append("xhtml:div"); let label = node.label; if (node.label && hasKatex(node.label)) { label = await renderKatex(node.label.replace(common_default.lineBreakRegex, "\n"), getConfig2()); } const labelClass = node.isNode ? "nodeLabel" : "edgeLabel"; div.html( '<span class="' + labelClass + '" ' + (node.labelStyle ? 'style="' + node.labelStyle + '"' : "") + // codeql [js/html-constructed-from-input] : false positive ">" + label + "</span>" ); applyStyle(div, node.labelStyle); div.style("display", "inline-block"); div.style("padding-right", "1px"); div.style("white-space", "nowrap"); div.attr("xmlns", "http://www.w3.org/1999/xhtml"); return fo.node(); } __name(addHtmlLabel, "addHtmlLabel"); var createLabel = /* @__PURE__ */ __name(async (_vertexText, style, isTitle, isNode) => { let vertexText = _vertexText || ""; if (typeof vertexText === "object") { vertexText = vertexText[0]; } if (evaluate(getConfig2().flowchart.htmlLabels)) { vertexText = vertexText.replace(/\\n|\n/g, "<br />"); log.info("vertexText" + vertexText); const node = { isNode, label: decodeEntities(vertexText).replace( /fa[blrs]?:fa-[\w-]+/g, (s) => `<i class='${s.replace(":", " ")}'></i>` ), labelStyle: style ? style.replace("fill:", "color:") : style }; let vertexNode = await addHtmlLabel(node); return vertexNode; } else { const svgLabel = document.createElementNS("http://www.w3.org/2000/svg", "text"); svgLabel.setAttribute("style", style.replace("color:", "fill:")); let rows = []; if (typeof vertexText === "string") { rows = vertexText.split(/\\n|\n|<br\s*\/?>/gi); } else if (Array.isArray(vertexText)) { rows = vertexText; } else { rows = []; } for (const row of rows) { const tspan = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); tspan.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:space", "preserve"); tspan.setAttribute("dy", "1em"); tspan.setAttribute("x", "0"); if (isTitle) { tspan.setAttribute("class", "title-row"); } else { tspan.setAttribute("class", "row"); } tspan.textContent = row.trim(); svgLabel.appendChild(tspan); } return svgLabel; } }, "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.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.id).attr("data-look", node.look); const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels); const labelEl = shapeSvg.insert("g").attr("class", "cluster-label "); const text2 = await createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels, isNode: true }); let bbox = text2.getBBox(); if (evaluate(siteConfig.flowchart.htmlLabels)) { const div = text2.children[0]; const dv = select3(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 = rough.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.id); 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.id).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 = label.node().appendChild(await createLabel_default(node.label, node.labelStyle, void 0, true)); let bbox = text2.getBBox(); if (evaluate(siteConfig.flowchart.htmlLabels)) { const div = text2.children[0]; const dv = select3(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 = rough.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 - (evaluate(siteConfig.flowchart.htmlLabels) ? 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.id).attr("data-look", node.look); const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels); 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 (evaluate(siteConfig.flowchart.htmlLabels)) { const div = text2.children[0]; const dv = select3(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 = rough.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.id).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 = rough.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"); const 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 }; 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) { var a1, a2, b1, b2, c1, c2; var r1, r2, r3, r4; var denom, offset, num; var x, y; a1 = p2.y - p1.y; b1 = p1.x - p2.x; c1 = p2.x * p1.y - p1.x * p2.y; r3 = a1 * q1.x + b1 * q1.y + c1; r4 = a1 * q2.x + b1 * q2.y + c1; if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { return; } a2 = q2.y - q1.y; b2 = q1.x - q2.x; c2 = q2.x * q1.y - q1.x * q2.y; r1 = a2 * p1.x + b2 * p1.y + c2; r2 = a2 * p2.x + b2 * p2.y + c2; if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) { return; } denom = a1 * b2 - a2 * b1; if (denom === 0) { return; } offset = Math.abs(denom / 2); num = b1 * c2 - b2 * c1; x = num < 0 ? (num - offset) / denom : (num + offset) / denom; num = a2 * c1 - a1 * c2; 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 import rough2 from "roughjs"; 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 = rough2.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 import rough3 from "roughjs"; 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"); async function bowTieRect(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); const w = bbox.width + node.padding + 20; const h = bbox.height + node.padding; const ry = h / 2; const rx = ry / (2.5 + h / 50); 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 = rough3.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"); 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/card.ts import rough4 from "roughjs"; // 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 async function card(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); const h = bbox.height + node.padding; const padding = 12; const w = bbox.width + node.padding + padding; const left = 0; const right = w; const top = -h; const bottom = 0; const points = [ { x: left + padding, y: top }, { x: right, y: top }, { x: right, y: bottom }, { x: left, y: bottom }, { x: left, y: top + padding }, { x: left + padding, y: top } ]; let polygon; const { cssStyles } = node; if (node.look === "handDrawn") { const rc = rough4.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 import rough5 from "roughjs"; 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 = rough5.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 import rough6 from "roughjs"; async function circle(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node)); const radius = bbox.width / 2 + halfPadding; let circleElem; const { cssStyles } = node; if (node.look === "handDrawn") { const rc = rough6.svg(shapeSvg); const options = userNodeOverrides(node, {}); const roughNode = rc.circle(0, 0, radius * 2, options); 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.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 import rough7 from "roughjs"; 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 = rough7.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); 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 import rough8 from "roughjs"; 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 w = bbox.width + (node.padding ?? 0); const h = bbox.height + (node.padding ?? 0); 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 = rough8.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 import rough9 from "roughjs"; 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 w = bbox.width + (node.padding ?? 0); const h = bbox.height + (node.padding ?? 0); 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, radius, 20, -90, -180), { x: w / 2 + radius, y: h / 2 }, ...generateCirclePoints3(w / 2, h / 2, radius, 20, 0, 90), { x: w / 2, y: h / 2 + radius }, { x: -w / 2, y: h / 2 + radius } ]; const rc = rough9.svg(shapeSvg); const options = userNodeOverrides(node, { fill: "none" }); if (node.look !== "handDrawn") { options.roughness = 0; options.fillStyle = "solid"; } const curlyBraceRightPath = createPathFromPoints(points); const newCurlyBracePath = curlyBraceRightPath.replace("Z", ""); const curlyBraceRightNode = rc.path(newCurlyBracePath, options); const rectPath = createPathFromPoints(rectPoints); const rectShape = rc.path(rectPath, { ...options }); const curlyBraceRightShape = shapeSvg.insert("g", ":first-child"); curlyBraceRightShape.insert(() => rectShape, ":first-child").attr("stroke-opacity", 0); curlyBraceRightShape.insert(() => curlyBraceRightNode, ":first-child"); curlyBraceRightShape.attr("class", "text"); if (cssStyles && node.look !== "handDrawn") { curlyBraceRightShape.selectAll("path").attr("style", cssStyles); } if (nodeStyles && node.look !== "handDrawn") { curlyBraceRightShape.selectAll("path").attr("style", nodeStyles); } curlyBraceRightShape.attr("transform", `translate(${-radius}, 0)`); label.attr( "transform", `translate(${-w / 2 + (node.padding ?? 0) / 2 - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` ); updateNodeBounds(node, curlyBraceRightShape); node.intersect = function(point) { const pos = intersect_default.polygon(node, rectPoints, point); return pos; }; return shapeSvg; } __name(curlyBraceRight, "curlyBraceRight"); // src/rendering-util/rendering-elements/shapes/curlyBraces.ts import rough10 from "roughjs"; function generateCirclePoints4(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(generateCirclePoints4, "generateCirclePoints"); async function curlyBraces(parent, node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); const w = bbox.width + (node.padding ?? 0); const h = bbox.height + (node.padding ?? 0); const radius = Math.max(5, h * 0.1); const { cssStyles } = node; const leftCurlyBracePoints = [ ...generateCirclePoints4(w / 2, -h / 2, radius, 30, -90, 0), { x: -w / 2 - radius, y: radius }, ...generateCirclePoints4(w / 2 + radius * 2, -radius, radius, 20, -180, -270), ...generateCirclePoints4(w / 2 + radius * 2, radius, radius, 20, -90, -180), { x: -w / 2 - radius, y: -h / 2 }, ...generateCirclePoints4(w / 2, h / 2, radius, 20, 0, 90) ]; const rightCurlyBracePoints = [ ...generateCirclePoints4(-w / 2 + radius + radius / 2, -h / 2, radius, 20, -90, -180), { x: w / 2 - radius / 2, y: radius }, ...generateCirclePoints4(-w / 2 - radius / 2, -radius, radius, 20, 0, 90), ...generateCirclePoints4(-w / 2 - radius / 2, radius, radius, 20, -90, 0), { x: w / 2 - radius / 2, y: -radius }, ...generateCirclePoints4(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270) ]; const rectPoints = [ { x: w / 2, y: -h / 2 - radius }, { x: -w / 2, y: -h / 2 - radius }, ...generateCirclePoints4(w / 2, -h / 2, radius, 20, -90, 0), { x: -w / 2 - radius, y: -radius }, ...generateCirclePoints4(w / 2 + radius * 2, -radius, radius, 20, -180, -270), ...generateCirclePoints4(w / 2 + radius * 2, radius, radius, 20, -90, -180), { x: -w / 2 - radius, y: h / 2 }, ...generateCirclePoints4(w / 2, h / 2, radius, 20, 0, 90), { x: -w / 2, y: h / 2 + radius }, { x: w / 2 - radius - radius / 2, y: h / 2 + radius }, ...generateCirclePoints4(-w / 2 + radius + radius / 2, -h / 2, radius, 20, -90, -180), { x: w / 2 - radius / 2, y: radius }, ...generateCirclePoints4(-w / 2 - radius / 2, -radius, radius, 20, 0, 90), ...generateCirclePoints4(-w / 2 - radius / 2, radius, radius, 20, -90, 0), { x: w / 2 - radius / 2, y: -radius }, ...generateCirclePoints4(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270) ]; const rc = rough10.svg(shapeSvg); const options = userNodeOverrides(node, { fill: "none" }); if (node.look !== "handDrawn") { options.roughness = 0; options.fillStyle = "solid"; } const leftCurlyBracePath = createPathFromPoints(leftCurlyBracePoints); const newLeftCurlyBracePath = leftCurlyBracePath.replace("Z", ""); const leftCurlyBraceNode = rc.path(newLeftCurlyBracePath, options); const rightCurlyBracePath = createPathFromPoints(rightCurlyBracePoints); const newRightCurlyBracePath = rightCurlyBracePath.replace("Z", ""); const rightCurlyBraceNode = rc.path(newRightCurlyBracePath, options); const rectPath = createPathFromPoints(rectPoints); const rectShape = rc.path(rectPath, { ...options }); const curlyBracesShape = shapeSvg.insert("g", ":first-child"); curlyBracesShape.insert(() => rectShape, ":first-child").attr("stroke-opacity", 0); curlyBracesShape.insert(() => leftCurlyBraceNode, ":first-child"); curlyBracesShape.insert(() => rightCurlyBraceNode, ":first-child"); curlyBracesShape.attr("class", "text"); if (cssStyles && node.look !== "handDrawn") { curlyBracesShape.selectAll("path").attr("style", cssStyles); } if (nodeStyles && node.look !== "handDrawn") { curlyBracesShape.selectAll("path").attr("style", nodeStyles); } curlyBracesShape.attr("transform", `translate(${radius - radius / 4}, 0)`); label.attr( "transform", `translate(${-w / 2 + (node.padding ?? 0) / 2 - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` ); updateNodeBounds(node, curlyBracesShape); node.intersect = function(point) { const pos = intersect_default.polygon(node, rectPoin