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
JavaScript
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