All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.dist.chunks.mermaid.core.architectureDiagram-AYX4OTIS.mjs Maven / Gradle / Ivy

Go to download

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
};




© 2015 - 2024 Weber Informatics LLC | Privacy Policy