package.dist.chunks.mermaid.core.architectureDiagram-AYX4OTIS.mjs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mermaid Show documentation
Show all versions of mermaid Show documentation
Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.
The newest version!
import {
getIconSVG,
registerIconPacks,
unknownIcon
} from "./chunk-32DZJKXY.mjs";
import {
createText
} from "./chunk-U6O4IJP6.mjs";
import {
populateCommonDb
} from "./chunk-BAOP5US2.mjs";
import {
ImperativeState
} from "./chunk-RGXPSUNZ.mjs";
import "./chunk-MCANT3UC.mjs";
import {
selectSvgElement
} from "./chunk-HK56VNYQ.mjs";
import {
__name,
clear,
defaultConfig_default,
getAccDescription,
getAccTitle,
getConfig2 as getConfig,
getDiagramTitle,
log,
setAccDescription,
setAccTitle,
setDiagramTitle,
setupGraphViewbox
} from "./chunk-P27NXTFD.mjs";
// src/diagrams/architecture/architectureParser.ts
import { parse } from "@mermaid-js/parser";
// src/diagrams/architecture/architectureTypes.ts
var ArchitectureDirectionName = {
L: "left",
R: "right",
T: "top",
B: "bottom"
};
var ArchitectureDirectionArrow = {
L: /* @__PURE__ */ __name((scale) => `${scale},${scale / 2} 0,${scale} 0,0`, "L"),
R: /* @__PURE__ */ __name((scale) => `0,${scale / 2} ${scale},0 ${scale},${scale}`, "R"),
T: /* @__PURE__ */ __name((scale) => `0,0 ${scale},0 ${scale / 2},${scale}`, "T"),
B: /* @__PURE__ */ __name((scale) => `${scale / 2},0 ${scale},${scale} 0,${scale}`, "B")
};
var ArchitectureDirectionArrowShift = {
L: /* @__PURE__ */ __name((orig, arrowSize) => orig - arrowSize + 2, "L"),
R: /* @__PURE__ */ __name((orig, _arrowSize) => orig - 2, "R"),
T: /* @__PURE__ */ __name((orig, arrowSize) => orig - arrowSize + 2, "T"),
B: /* @__PURE__ */ __name((orig, _arrowSize) => orig - 2, "B")
};
var getOppositeArchitectureDirection = /* @__PURE__ */ __name(function(x) {
if (isArchitectureDirectionX(x)) {
return x === "L" ? "R" : "L";
} else {
return x === "T" ? "B" : "T";
}
}, "getOppositeArchitectureDirection");
var isArchitectureDirection = /* @__PURE__ */ __name(function(x) {
const temp = x;
return temp === "L" || temp === "R" || temp === "T" || temp === "B";
}, "isArchitectureDirection");
var isArchitectureDirectionX = /* @__PURE__ */ __name(function(x) {
const temp = x;
return temp === "L" || temp === "R";
}, "isArchitectureDirectionX");
var isArchitectureDirectionY = /* @__PURE__ */ __name(function(x) {
const temp = x;
return temp === "T" || temp === "B";
}, "isArchitectureDirectionY");
var isArchitectureDirectionXY = /* @__PURE__ */ __name(function(a, b) {
const aX_bY = isArchitectureDirectionX(a) && isArchitectureDirectionY(b);
const aY_bX = isArchitectureDirectionY(a) && isArchitectureDirectionX(b);
return aX_bY || aY_bX;
}, "isArchitectureDirectionXY");
var isArchitecturePairXY = /* @__PURE__ */ __name(function(pair) {
const lhs = pair[0];
const rhs = pair[1];
const aX_bY = isArchitectureDirectionX(lhs) && isArchitectureDirectionY(rhs);
const aY_bX = isArchitectureDirectionY(lhs) && isArchitectureDirectionX(rhs);
return aX_bY || aY_bX;
}, "isArchitecturePairXY");
var isValidArchitectureDirectionPair = /* @__PURE__ */ __name(function(x) {
return x !== "LL" && x !== "RR" && x !== "TT" && x !== "BB";
}, "isValidArchitectureDirectionPair");
var getArchitectureDirectionPair = /* @__PURE__ */ __name(function(sourceDir, targetDir) {
const pair = `${sourceDir}${targetDir}`;
return isValidArchitectureDirectionPair(pair) ? pair : void 0;
}, "getArchitectureDirectionPair");
var shiftPositionByArchitectureDirectionPair = /* @__PURE__ */ __name(function([x, y], pair) {
const lhs = pair[0];
const rhs = pair[1];
if (isArchitectureDirectionX(lhs)) {
if (isArchitectureDirectionY(rhs)) {
return [x + (lhs === "L" ? -1 : 1), y + (rhs === "T" ? 1 : -1)];
} else {
return [x + (lhs === "L" ? -1 : 1), y];
}
} else {
if (isArchitectureDirectionX(rhs)) {
return [x + (rhs === "L" ? 1 : -1), y + (lhs === "T" ? 1 : -1)];
} else {
return [x, y + (lhs === "T" ? 1 : -1)];
}
}
}, "shiftPositionByArchitectureDirectionPair");
var getArchitectureDirectionXYFactors = /* @__PURE__ */ __name(function(pair) {
if (pair === "LT" || pair === "TL") {
return [1, 1];
} else if (pair === "BL" || pair === "LB") {
return [1, -1];
} else if (pair === "BR" || pair === "RB") {
return [-1, -1];
} else {
return [-1, 1];
}
}, "getArchitectureDirectionXYFactors");
var isArchitectureService = /* @__PURE__ */ __name(function(x) {
const temp = x;
return temp.type === "service";
}, "isArchitectureService");
var isArchitectureJunction = /* @__PURE__ */ __name(function(x) {
const temp = x;
return temp.type === "junction";
}, "isArchitectureJunction");
var edgeData = /* @__PURE__ */ __name((edge) => {
return edge.data();
}, "edgeData");
var nodeData = /* @__PURE__ */ __name((node) => {
return node.data();
}, "nodeData");
// src/diagrams/architecture/architectureDb.ts
var DEFAULT_ARCHITECTURE_CONFIG = defaultConfig_default.architecture;
var state = new ImperativeState(() => ({
nodes: {},
groups: {},
edges: [],
registeredIds: {},
config: DEFAULT_ARCHITECTURE_CONFIG,
dataStructures: void 0,
elements: {}
}));
var clear2 = /* @__PURE__ */ __name(() => {
state.reset();
clear();
}, "clear");
var addService = /* @__PURE__ */ __name(function({
id,
icon,
in: parent,
title,
iconText
}) {
if (state.records.registeredIds[id] !== void 0) {
throw new Error(
`The service id [${id}] is already in use by another ${state.records.registeredIds[id]}`
);
}
if (parent !== void 0) {
if (id === parent) {
throw new Error(`The service [${id}] cannot be placed within itself`);
}
if (state.records.registeredIds[parent] === void 0) {
throw new Error(
`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`
);
}
if (state.records.registeredIds[parent] === "node") {
throw new Error(`The service [${id}]'s parent is not a group`);
}
}
state.records.registeredIds[id] = "node";
state.records.nodes[id] = {
id,
type: "service",
icon,
iconText,
title,
edges: [],
in: parent
};
}, "addService");
var getServices = /* @__PURE__ */ __name(() => Object.values(state.records.nodes).filter(isArchitectureService), "getServices");
var addJunction = /* @__PURE__ */ __name(function({ id, in: parent }) {
state.records.registeredIds[id] = "node";
state.records.nodes[id] = {
id,
type: "junction",
edges: [],
in: parent
};
}, "addJunction");
var getJunctions = /* @__PURE__ */ __name(() => Object.values(state.records.nodes).filter(isArchitectureJunction), "getJunctions");
var getNodes = /* @__PURE__ */ __name(() => Object.values(state.records.nodes), "getNodes");
var getNode = /* @__PURE__ */ __name((id) => state.records.nodes[id], "getNode");
var addGroup = /* @__PURE__ */ __name(function({ id, icon, in: parent, title }) {
if (state.records.registeredIds[id] !== void 0) {
throw new Error(
`The group id [${id}] is already in use by another ${state.records.registeredIds[id]}`
);
}
if (parent !== void 0) {
if (id === parent) {
throw new Error(`The group [${id}] cannot be placed within itself`);
}
if (state.records.registeredIds[parent] === void 0) {
throw new Error(
`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`
);
}
if (state.records.registeredIds[parent] === "node") {
throw new Error(`The group [${id}]'s parent is not a group`);
}
}
state.records.registeredIds[id] = "group";
state.records.groups[id] = {
id,
icon,
title,
in: parent
};
}, "addGroup");
var getGroups = /* @__PURE__ */ __name(() => {
return Object.values(state.records.groups);
}, "getGroups");
var addEdge = /* @__PURE__ */ __name(function({
lhsId,
rhsId,
lhsDir,
rhsDir,
lhsInto,
rhsInto,
lhsGroup,
rhsGroup,
title
}) {
if (!isArchitectureDirection(lhsDir)) {
throw new Error(
`Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${lhsDir}`
);
}
if (!isArchitectureDirection(rhsDir)) {
throw new Error(
`Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${rhsDir}`
);
}
if (state.records.nodes[lhsId] === void 0 && state.records.groups[lhsId] === void 0) {
throw new Error(
`The left-hand id [${lhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
}
if (state.records.nodes[rhsId] === void 0 && state.records.groups[lhsId] === void 0) {
throw new Error(
`The right-hand id [${rhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
}
const lhsGroupId = state.records.nodes[lhsId].in;
const rhsGroupId = state.records.nodes[rhsId].in;
if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
}
if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
}
const edge = {
lhsId,
lhsDir,
lhsInto,
lhsGroup,
rhsId,
rhsDir,
rhsInto,
rhsGroup,
title
};
state.records.edges.push(edge);
if (state.records.nodes[lhsId] && state.records.nodes[rhsId]) {
state.records.nodes[lhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
state.records.nodes[rhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
}
}, "addEdge");
var getEdges = /* @__PURE__ */ __name(() => state.records.edges, "getEdges");
var getDataStructures = /* @__PURE__ */ __name(() => {
if (state.records.dataStructures === void 0) {
const adjList = Object.entries(state.records.nodes).reduce((prevOuter, [id, service]) => {
prevOuter[id] = service.edges.reduce((prevInner, edge) => {
if (edge.lhsId === id) {
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
if (pair) {
prevInner[pair] = edge.rhsId;
}
} else {
const pair = getArchitectureDirectionPair(edge.rhsDir, edge.lhsDir);
if (pair) {
prevInner[pair] = edge.lhsId;
}
}
return prevInner;
}, {});
return prevOuter;
}, {});
const firstId = Object.keys(adjList)[0];
const visited = { [firstId]: 1 };
const notVisited = Object.keys(adjList).reduce(
(prev, id) => id === firstId ? prev : { ...prev, [id]: 1 },
{}
);
const BFS = /* @__PURE__ */ __name((startingId) => {
const spatialMap = { [startingId]: [0, 0] };
const queue = [startingId];
while (queue.length > 0) {
const id = queue.shift();
if (id) {
visited[id] = 1;
delete notVisited[id];
const adj = adjList[id];
const [posX, posY] = spatialMap[id];
Object.entries(adj).forEach(([dir, rhsId]) => {
if (!visited[rhsId]) {
spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair(
[posX, posY],
dir
);
queue.push(rhsId);
}
});
}
}
return spatialMap;
}, "BFS");
const spatialMaps = [BFS(firstId)];
while (Object.keys(notVisited).length > 0) {
spatialMaps.push(BFS(Object.keys(notVisited)[0]));
}
state.records.dataStructures = {
adjList,
spatialMaps
};
}
return state.records.dataStructures;
}, "getDataStructures");
var setElementForId = /* @__PURE__ */ __name((id, element) => {
state.records.elements[id] = element;
}, "setElementForId");
var getElementById = /* @__PURE__ */ __name((id) => state.records.elements[id], "getElementById");
var db = {
clear: clear2,
setDiagramTitle,
getDiagramTitle,
setAccTitle,
getAccTitle,
setAccDescription,
getAccDescription,
addService,
getServices,
addJunction,
getJunctions,
getNodes,
getNode,
addGroup,
getGroups,
addEdge,
getEdges,
setElementForId,
getElementById,
getDataStructures
};
function getConfigField(field) {
const arch = getConfig().architecture;
if (arch?.[field]) {
return arch[field];
}
return DEFAULT_ARCHITECTURE_CONFIG[field];
}
__name(getConfigField, "getConfigField");
// src/diagrams/architecture/architectureParser.ts
var populateDb = /* @__PURE__ */ __name((ast, db2) => {
populateCommonDb(ast, db2);
ast.groups.map(db2.addGroup);
ast.services.map((service) => db2.addService({ ...service, type: "service" }));
ast.junctions.map((service) => db2.addJunction({ ...service, type: "junction" }));
ast.edges.map(db2.addEdge);
}, "populateDb");
var parser = {
parse: /* @__PURE__ */ __name(async (input) => {
const ast = await parse("architecture", input);
log.debug(ast);
populateDb(ast, db);
}, "parse")
};
// src/diagrams/architecture/architectureStyles.ts
var getStyles = /* @__PURE__ */ __name((options) => `
.edge {
stroke-width: ${options.archEdgeWidth};
stroke: ${options.archEdgeColor};
fill: none;
}
.arrow {
fill: ${options.archEdgeArrowColor};
}
.node-bkg {
fill: none;
stroke: ${options.archGroupBorderColor};
stroke-width: ${options.archGroupBorderWidth};
stroke-dasharray: 8;
}
.node-icon-text {
display: flex;
align-items: center;
}
.node-icon-text > div {
color: #fff;
margin: 1px;
height: fit-content;
text-align: center;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
}
`, "getStyles");
var architectureStyles_default = getStyles;
// src/diagrams/architecture/architectureRenderer.ts
import cytoscape from "cytoscape";
import fcose from "cytoscape-fcose";
import { select } from "d3";
// src/diagrams/architecture/architectureIcons.ts
var wrapIcon = /* @__PURE__ */ __name((icon) => {
return ` ${icon} `;
}, "wrapIcon");
var architectureIcons = {
prefix: "mermaid-architecture",
height: 80,
width: 80,
icons: {
database: {
body: wrapIcon(
' '
)
},
server: {
body: wrapIcon(
' '
)
},
disk: {
body: wrapIcon(
' '
)
},
internet: {
body: wrapIcon(
' '
)
},
cloud: {
body: wrapIcon(
' '
)
},
unknown: unknownIcon,
blank: {
body: wrapIcon("")
}
}
};
// src/diagrams/architecture/svgDraw.ts
var drawEdges = /* @__PURE__ */ __name(async function(edgesEl, cy) {
const padding = getConfigField("padding");
const iconSize = getConfigField("iconSize");
const halfIconSize = iconSize / 2;
const arrowSize = iconSize / 6;
const halfArrowSize = arrowSize / 2;
await Promise.all(
cy.edges().map(async (edge) => {
const {
source,
sourceDir,
sourceArrow,
sourceGroup,
target,
targetDir,
targetArrow,
targetGroup,
label
} = edgeData(edge);
let { x: startX, y: startY } = edge[0].sourceEndpoint();
const { x: midX, y: midY } = edge[0].midpoint();
let { x: endX, y: endY } = edge[0].targetEndpoint();
const groupEdgeShift = padding + 4;
if (sourceGroup) {
if (isArchitectureDirectionX(sourceDir)) {
startX += sourceDir === "L" ? -groupEdgeShift : groupEdgeShift;
} else {
startY += sourceDir === "T" ? -groupEdgeShift : groupEdgeShift + 18;
}
}
if (targetGroup) {
if (isArchitectureDirectionX(targetDir)) {
endX += targetDir === "L" ? -groupEdgeShift : groupEdgeShift;
} else {
endY += targetDir === "T" ? -groupEdgeShift : groupEdgeShift + 18;
}
}
if (!sourceGroup && db.getNode(source)?.type === "junction") {
if (isArchitectureDirectionX(sourceDir)) {
startX += sourceDir === "L" ? halfIconSize : -halfIconSize;
} else {
startY += sourceDir === "T" ? halfIconSize : -halfIconSize;
}
}
if (!targetGroup && db.getNode(target)?.type === "junction") {
if (isArchitectureDirectionX(targetDir)) {
endX += targetDir === "L" ? halfIconSize : -halfIconSize;
} else {
endY += targetDir === "T" ? halfIconSize : -halfIconSize;
}
}
if (edge[0]._private.rscratch) {
const g = edgesEl.insert("g");
g.insert("path").attr("d", `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `).attr("class", "edge");
if (sourceArrow) {
const xShift = isArchitectureDirectionX(sourceDir) ? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize) : startX - halfArrowSize;
const yShift = isArchitectureDirectionY(sourceDir) ? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize) : startY - halfArrowSize;
g.insert("polygon").attr("points", ArchitectureDirectionArrow[sourceDir](arrowSize)).attr("transform", `translate(${xShift},${yShift})`).attr("class", "arrow");
}
if (targetArrow) {
const xShift = isArchitectureDirectionX(targetDir) ? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize) : endX - halfArrowSize;
const yShift = isArchitectureDirectionY(targetDir) ? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize) : endY - halfArrowSize;
g.insert("polygon").attr("points", ArchitectureDirectionArrow[targetDir](arrowSize)).attr("transform", `translate(${xShift},${yShift})`).attr("class", "arrow");
}
if (label) {
const axis = !isArchitectureDirectionXY(sourceDir, targetDir) ? isArchitectureDirectionX(sourceDir) ? "X" : "Y" : "XY";
let width = 0;
if (axis === "X") {
width = Math.abs(startX - endX);
} else if (axis === "Y") {
width = Math.abs(startY - endY) / 1.5;
} else {
width = Math.abs(startX - endX) / 2;
}
const textElem = g.append("g");
await createText(
textElem,
label,
{
useHtmlLabels: false,
width,
classes: "architecture-service-label"
},
getConfig()
);
textElem.attr("dy", "1em").attr("alignment-baseline", "middle").attr("dominant-baseline", "middle").attr("text-anchor", "middle");
if (axis === "X") {
textElem.attr("transform", "translate(" + midX + ", " + midY + ")");
} else if (axis === "Y") {
textElem.attr("transform", "translate(" + midX + ", " + midY + ") rotate(-90)");
} else if (axis === "XY") {
const pair = getArchitectureDirectionPair(sourceDir, targetDir);
if (pair && isArchitecturePairXY(pair)) {
const bboxOrig = textElem.node().getBoundingClientRect();
const [x, y] = getArchitectureDirectionXYFactors(pair);
textElem.attr("dominant-baseline", "auto").attr("transform", `rotate(${-1 * x * y * 45})`);
const bboxNew = textElem.node().getBoundingClientRect();
textElem.attr(
"transform",
`
translate(${midX}, ${midY - bboxOrig.height / 2})
translate(${x * bboxNew.width / 2}, ${y * bboxNew.height / 2})
rotate(${-1 * x * y * 45}, 0, ${bboxOrig.height / 2})
`
);
}
}
}
}
})
);
}, "drawEdges");
var drawGroups = /* @__PURE__ */ __name(async function(groupsEl, cy) {
const padding = getConfigField("padding");
const groupIconSize = padding * 0.75;
const fontSize = getConfigField("fontSize");
const iconSize = getConfigField("iconSize");
const halfIconSize = iconSize / 2;
await Promise.all(
cy.nodes().map(async (node) => {
const data = nodeData(node);
if (data.type === "group") {
const { h, w, x1, y1 } = node.boundingBox();
groupsEl.append("rect").attr("x", x1 + halfIconSize).attr("y", y1 + halfIconSize).attr("width", w).attr("height", h).attr("class", "node-bkg");
const groupLabelContainer = groupsEl.append("g");
let shiftedX1 = x1;
let shiftedY1 = y1;
if (data.icon) {
const bkgElem = groupLabelContainer.append("g");
bkgElem.html(
`${await getIconSVG(data.icon, { height: groupIconSize, width: groupIconSize, fallbackPrefix: architectureIcons.prefix })} `
);
bkgElem.attr(
"transform",
"translate(" + (shiftedX1 + halfIconSize + 1) + ", " + (shiftedY1 + halfIconSize + 1) + ")"
);
shiftedX1 += groupIconSize;
shiftedY1 += fontSize / 2 - 1 - 2;
}
if (data.label) {
const textElem = groupLabelContainer.append("g");
await createText(
textElem,
data.label,
{
useHtmlLabels: false,
width: w,
classes: "architecture-service-label"
},
getConfig()
);
textElem.attr("dy", "1em").attr("alignment-baseline", "middle").attr("dominant-baseline", "start").attr("text-anchor", "start");
textElem.attr(
"transform",
"translate(" + (shiftedX1 + halfIconSize + 4) + ", " + (shiftedY1 + halfIconSize + 2) + ")"
);
}
}
})
);
}, "drawGroups");
var drawServices = /* @__PURE__ */ __name(async function(db2, elem, services) {
for (const service of services) {
const serviceElem = elem.append("g");
const iconSize = getConfigField("iconSize");
if (service.title) {
const textElem = serviceElem.append("g");
await createText(
textElem,
service.title,
{
useHtmlLabels: false,
width: iconSize * 1.5,
classes: "architecture-service-label"
},
getConfig()
);
textElem.attr("dy", "1em").attr("alignment-baseline", "middle").attr("dominant-baseline", "middle").attr("text-anchor", "middle");
textElem.attr("transform", "translate(" + iconSize / 2 + ", " + iconSize + ")");
}
const bkgElem = serviceElem.append("g");
if (service.icon) {
bkgElem.html(
`${await getIconSVG(service.icon, { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })} `
);
} else if (service.iconText) {
bkgElem.html(
`${await getIconSVG("blank", { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })} `
);
const textElemContainer = bkgElem.append("g");
const fo = textElemContainer.append("foreignObject").attr("width", iconSize).attr("height", iconSize);
const divElem = fo.append("div").attr("class", "node-icon-text").attr("style", `height: ${iconSize}px;`).append("div").html(service.iconText);
const fontSize = parseInt(
window.getComputedStyle(divElem.node(), null).getPropertyValue("font-size").replace(/\D/g, "")
) ?? 16;
divElem.attr("style", `-webkit-line-clamp: ${Math.floor((iconSize - 2) / fontSize)};`);
} else {
bkgElem.append("path").attr("class", "node-bkg").attr("id", "node-" + service.id).attr(
"d",
`M0 ${iconSize} v${-iconSize} q0,-5 5,-5 h${iconSize} q5,0 5,5 v${iconSize} H0 Z`
);
}
serviceElem.attr("class", "architecture-service");
const { width, height } = serviceElem._groups[0][0].getBBox();
service.width = width;
service.height = height;
db2.setElementForId(service.id, serviceElem);
}
return 0;
}, "drawServices");
var drawJunctions = /* @__PURE__ */ __name(function(db2, elem, junctions) {
junctions.forEach((junction) => {
const junctionElem = elem.append("g");
const iconSize = getConfigField("iconSize");
const bkgElem = junctionElem.append("g");
bkgElem.append("rect").attr("id", "node-" + junction.id).attr("fill-opacity", "0").attr("width", iconSize).attr("height", iconSize);
junctionElem.attr("class", "architecture-junction");
const { width, height } = junctionElem._groups[0][0].getBBox();
junctionElem.width = width;
junctionElem.height = height;
db2.setElementForId(junction.id, junctionElem);
});
}, "drawJunctions");
// src/diagrams/architecture/architectureRenderer.ts
registerIconPacks([
{
name: architectureIcons.prefix,
icons: architectureIcons
}
]);
cytoscape.use(fcose);
function addServices(services, cy) {
services.forEach((service) => {
cy.add({
group: "nodes",
data: {
type: "service",
id: service.id,
icon: service.icon,
label: service.title,
parent: service.in,
width: getConfigField("iconSize"),
height: getConfigField("iconSize")
},
classes: "node-service"
});
});
}
__name(addServices, "addServices");
function addJunctions(junctions, cy) {
junctions.forEach((junction) => {
cy.add({
group: "nodes",
data: {
type: "junction",
id: junction.id,
parent: junction.in,
width: getConfigField("iconSize"),
height: getConfigField("iconSize")
},
classes: "node-junction"
});
});
}
__name(addJunctions, "addJunctions");
function positionNodes(db2, cy) {
cy.nodes().map((node) => {
const data = nodeData(node);
if (data.type === "group") {
return;
}
data.x = node.position().x;
data.y = node.position().y;
const nodeElem = db2.getElementById(data.id);
nodeElem.attr("transform", "translate(" + (data.x || 0) + "," + (data.y || 0) + ")");
});
}
__name(positionNodes, "positionNodes");
function addGroups(groups, cy) {
groups.forEach((group) => {
cy.add({
group: "nodes",
data: {
type: "group",
id: group.id,
icon: group.icon,
label: group.title,
parent: group.in
},
classes: "node-group"
});
});
}
__name(addGroups, "addGroups");
function addEdges(edges, cy) {
edges.forEach((parsedEdge) => {
const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } = parsedEdge;
const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir) ? "segments" : "straight";
const edge = {
id: `${lhsId}-${rhsId}`,
label: title,
source: lhsId,
sourceDir: lhsDir,
sourceArrow: lhsInto,
sourceGroup: lhsGroup,
sourceEndpoint: lhsDir === "L" ? "0 50%" : lhsDir === "R" ? "100% 50%" : lhsDir === "T" ? "50% 0" : "50% 100%",
target: rhsId,
targetDir: rhsDir,
targetArrow: rhsInto,
targetGroup: rhsGroup,
targetEndpoint: rhsDir === "L" ? "0 50%" : rhsDir === "R" ? "100% 50%" : rhsDir === "T" ? "50% 0" : "50% 100%"
};
cy.add({
group: "edges",
data: edge,
classes: edgeType
});
});
}
__name(addEdges, "addEdges");
function getAlignments(spatialMaps) {
const alignments = spatialMaps.map((spatialMap) => {
const horizontalAlignments = {};
const verticalAlignments = {};
Object.entries(spatialMap).forEach(([id, [x, y]]) => {
if (!horizontalAlignments[y]) {
horizontalAlignments[y] = [];
}
if (!verticalAlignments[x]) {
verticalAlignments[x] = [];
}
horizontalAlignments[y].push(id);
verticalAlignments[x].push(id);
});
return {
horiz: Object.values(horizontalAlignments).filter((arr) => arr.length > 1),
vert: Object.values(verticalAlignments).filter((arr) => arr.length > 1)
};
});
const [horizontal, vertical] = alignments.reduce(
([prevHoriz, prevVert], { horiz, vert }) => {
return [
[...prevHoriz, ...horiz],
[...prevVert, ...vert]
];
},
[[], []]
);
return {
horizontal,
vertical
};
}
__name(getAlignments, "getAlignments");
function getRelativeConstraints(spatialMaps) {
const relativeConstraints = [];
const posToStr = /* @__PURE__ */ __name((pos) => `${pos[0]},${pos[1]}`, "posToStr");
const strToPos = /* @__PURE__ */ __name((pos) => pos.split(",").map((p) => parseInt(p)), "strToPos");
spatialMaps.forEach((spatialMap) => {
const invSpatialMap = Object.fromEntries(
Object.entries(spatialMap).map(([id, pos]) => [posToStr(pos), id])
);
const queue = [posToStr([0, 0])];
const visited = {};
const directions = {
L: [-1, 0],
R: [1, 0],
T: [0, 1],
B: [0, -1]
};
while (queue.length > 0) {
const curr = queue.shift();
if (curr) {
visited[curr] = 1;
const currId = invSpatialMap[curr];
if (currId) {
const currPos = strToPos(curr);
Object.entries(directions).forEach(([dir, shift]) => {
const newPos = posToStr([currPos[0] + shift[0], currPos[1] + shift[1]]);
const newId = invSpatialMap[newPos];
if (newId && !visited[newPos]) {
queue.push(newPos);
relativeConstraints.push({
[ArchitectureDirectionName[dir]]: newId,
[ArchitectureDirectionName[getOppositeArchitectureDirection(dir)]]: currId,
gap: 1.5 * getConfigField("iconSize")
});
}
});
}
}
}
});
return relativeConstraints;
}
__name(getRelativeConstraints, "getRelativeConstraints");
function layoutArchitecture(services, junctions, groups, edges, { spatialMaps }) {
return new Promise((resolve) => {
const renderEl = select("body").append("div").attr("id", "cy").attr("style", "display:none");
const cy = cytoscape({
container: document.getElementById("cy"),
style: [
{
selector: "edge",
style: {
"curve-style": "straight",
label: "data(label)",
"source-endpoint": "data(sourceEndpoint)",
"target-endpoint": "data(targetEndpoint)"
}
},
{
selector: "edge.segments",
style: {
"curve-style": "segments",
"segment-weights": "0",
"segment-distances": [0.5],
// @ts-ignore Incorrect library types
"edge-distances": "endpoints",
"source-endpoint": "data(sourceEndpoint)",
"target-endpoint": "data(targetEndpoint)"
}
},
{
selector: "node",
style: {
// @ts-ignore Incorrect library types
"compound-sizing-wrt-labels": "include"
}
},
{
selector: "node[label]",
style: {
"text-valign": "bottom",
"text-halign": "center",
"font-size": `${getConfigField("fontSize")}px`
}
},
{
selector: ".node-service",
style: {
label: "data(label)",
width: "data(width)",
height: "data(height)"
}
},
{
selector: ".node-junction",
style: {
width: "data(width)",
height: "data(height)"
}
},
{
selector: ".node-group",
style: {
// @ts-ignore Incorrect library types
padding: `${getConfigField("padding")}px`
}
}
]
});
renderEl.remove();
addGroups(groups, cy);
addServices(services, cy);
addJunctions(junctions, cy);
addEdges(edges, cy);
const alignmentConstraint = getAlignments(spatialMaps);
const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
const layout = cy.layout({
name: "fcose",
quality: "proof",
styleEnabled: false,
animate: false,
nodeDimensionsIncludeLabels: false,
// Adjust the edge parameters if it passes through the border of a group
// Hacky fix for: https://github.com/iVis-at-Bilkent/cytoscape.js-fcose/issues/67
idealEdgeLength(edge) {
const [nodeA, nodeB] = edge.connectedNodes();
const { parent: parentA } = nodeData(nodeA);
const { parent: parentB } = nodeData(nodeB);
const elasticity = parentA === parentB ? 1.5 * getConfigField("iconSize") : 0.5 * getConfigField("iconSize");
return elasticity;
},
edgeElasticity(edge) {
const [nodeA, nodeB] = edge.connectedNodes();
const { parent: parentA } = nodeData(nodeA);
const { parent: parentB } = nodeData(nodeB);
const elasticity = parentA === parentB ? 0.45 : 1e-3;
return elasticity;
},
alignmentConstraint,
relativePlacementConstraint
});
layout.one("layoutstop", () => {
function getSegmentWeights(source, target, pointX, pointY) {
let W, D;
const { x: sX, y: sY } = source;
const { x: tX, y: tY } = target;
D = (pointY - sY + (sX - pointX) * (sY - tY) / (sX - tX)) / Math.sqrt(1 + Math.pow((sY - tY) / (sX - tX), 2));
W = Math.sqrt(Math.pow(pointY - sY, 2) + Math.pow(pointX - sX, 2) - Math.pow(D, 2));
const distAB = Math.sqrt(Math.pow(tX - sX, 2) + Math.pow(tY - sY, 2));
W = W / distAB;
let delta1 = (tX - sX) * (pointY - sY) - (tY - sY) * (pointX - sX);
switch (true) {
case delta1 >= 0:
delta1 = 1;
break;
case delta1 < 0:
delta1 = -1;
break;
}
let delta2 = (tX - sX) * (pointX - sX) + (tY - sY) * (pointY - sY);
switch (true) {
case delta2 >= 0:
delta2 = 1;
break;
case delta2 < 0:
delta2 = -1;
break;
}
D = Math.abs(D) * delta1;
W = W * delta2;
return {
distances: D,
weights: W
};
}
__name(getSegmentWeights, "getSegmentWeights");
cy.startBatch();
for (const edge of Object.values(cy.edges())) {
if (edge.data?.()) {
const { x: sX, y: sY } = edge.source().position();
const { x: tX, y: tY } = edge.target().position();
if (sX !== tX && sY !== tY) {
const sEP = edge.sourceEndpoint();
const tEP = edge.targetEndpoint();
const { sourceDir } = edgeData(edge);
const [pointX, pointY] = isArchitectureDirectionY(sourceDir) ? [sEP.x, tEP.y] : [tEP.x, sEP.y];
const { weights, distances } = getSegmentWeights(sEP, tEP, pointX, pointY);
edge.style("segment-distances", distances);
edge.style("segment-weights", weights);
}
}
}
cy.endBatch();
layout.run();
});
layout.run();
cy.ready((e) => {
log.info("Ready", e);
resolve(cy);
});
});
}
__name(layoutArchitecture, "layoutArchitecture");
var draw = /* @__PURE__ */ __name(async (text, id, _version, diagObj) => {
const db2 = diagObj.db;
const services = db2.getServices();
const junctions = db2.getJunctions();
const groups = db2.getGroups();
const edges = db2.getEdges();
const ds = db2.getDataStructures();
const svg = selectSvgElement(id);
const edgesElem = svg.append("g");
edgesElem.attr("class", "architecture-edges");
const servicesElem = svg.append("g");
servicesElem.attr("class", "architecture-services");
const groupElem = svg.append("g");
groupElem.attr("class", "architecture-groups");
await drawServices(db2, servicesElem, services);
drawJunctions(db2, servicesElem, junctions);
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
await drawEdges(edgesElem, cy);
await drawGroups(groupElem, cy);
positionNodes(db2, cy);
setupGraphViewbox(void 0, svg, getConfigField("padding"), getConfigField("useMaxWidth"));
}, "draw");
var renderer = { draw };
// src/diagrams/architecture/architectureDiagram.ts
var diagram = {
parser,
db,
renderer,
styles: architectureStyles_default
};
export {
diagram
};