mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-22 08:50:13 +02:00

``` C4Context title System Context diagram for Internet Banking System Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.") Person(customerB, "Banking Customer B") Person_Ext(customerC, "Banking Customer C") System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.") Person(customerD, "Banking Customer D", "A customer of the bank, <br/> with personal bank accounts.") Enterprise_Boundary(b1, "BankBoundary") { SystemDb_Ext(SystemE, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") System_Boundary(b2, "BankBoundary2") { System(SystemA, "Banking System A") System(SystemB, "Banking System B", "A system of the bank, with personal bank accounts.") } System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.") SystemDb(SystemD, "Banking System D Database", "A system of the bank, with personal bank accounts.") Boundary(b3, "BankBoundary3", "boundary") { SystemQueue(SystemF, "Banking System F Queue", "A system of the bank, with personal bank accounts.") SystemQueue_Ext(SystemG, "Banking System G Queue", "A system of the bank, with personal bank accounts.") } } BiRel(customerA, SystemAA, "Uses") BiRel(SystemAA, SystemE, "Uses") Rel(SystemAA, SystemC, "Sends e-mails", "SMTP") Rel(SystemC, customerA, "Sends e-mails to") ```
610 lines
19 KiB
JavaScript
610 lines
19 KiB
JavaScript
import { select } from 'd3';
|
|
import svgDraw, { drawText, fixLifeLineHeights } from './svgDraw';
|
|
import { log } from '../../logger';
|
|
import { parser } from './parser/c4Diagram';
|
|
import common from '../common/common';
|
|
import c4Db from './c4Db';
|
|
import * as configApi from '../../config';
|
|
import utils, {
|
|
wrapLabel,
|
|
calculateTextWidth,
|
|
calculateTextHeight,
|
|
assignWithDepth,
|
|
configureSvgSize,
|
|
} from '../../utils';
|
|
import addSVGAccessibilityFields from '../../accessibility';
|
|
|
|
let globalBoundaryMaxX = 0,
|
|
globalBoundaryMaxY = 0;
|
|
|
|
parser.yy = c4Db;
|
|
|
|
let conf = {};
|
|
|
|
class Bounds {
|
|
constructor() {
|
|
this.name = '';
|
|
this.data = {};
|
|
this.data.startx = undefined;
|
|
this.data.stopx = undefined;
|
|
this.data.starty = undefined;
|
|
this.data.stopy = undefined;
|
|
this.data.widthLimit = undefined;
|
|
|
|
this.nextData = {};
|
|
this.nextData.startx = undefined;
|
|
this.nextData.stopx = undefined;
|
|
this.nextData.starty = undefined;
|
|
this.nextData.stopy = undefined;
|
|
|
|
setConf(parser.yy.getConfig());
|
|
}
|
|
|
|
setData(startx, stopx, starty, stopy) {
|
|
this.nextData.startx = this.data.startx = startx;
|
|
this.nextData.stopx = this.data.stopx = stopx;
|
|
this.nextData.starty = this.data.starty = starty;
|
|
this.nextData.stopy = this.data.stopy = stopy;
|
|
}
|
|
|
|
updateVal(obj, key, val, fun) {
|
|
if (typeof obj[key] === 'undefined') {
|
|
obj[key] = val;
|
|
} else {
|
|
obj[key] = fun(val, obj[key]);
|
|
}
|
|
}
|
|
|
|
insert(c4Shape) {
|
|
let _startx = this.nextData.stopx + c4Shape.margin * 2;
|
|
let _stopx = _startx + c4Shape.width;
|
|
let _starty = this.nextData.starty + c4Shape.margin * 2;
|
|
let _stopy = _starty + c4Shape.height;
|
|
if (_startx >= this.data.widthLimit || _stopx >= this.data.widthLimit) {
|
|
_startx = this.nextData.startx + c4Shape.margin * 2 + conf.nextLinePaddingX;
|
|
_starty = this.nextData.stopy + c4Shape.margin * 2;
|
|
|
|
this.nextData.stopx = _stopx = _startx + c4Shape.width;
|
|
this.nextData.starty = this.nextData.stopy;
|
|
this.nextData.stopy = _stopy = _starty + c4Shape.height;
|
|
}
|
|
|
|
c4Shape.x = _startx;
|
|
c4Shape.y = _starty;
|
|
|
|
this.updateVal(this.data, 'startx', _startx, Math.min);
|
|
this.updateVal(this.data, 'starty', _starty, Math.min);
|
|
this.updateVal(this.data, 'stopx', _stopx, Math.max);
|
|
this.updateVal(this.data, 'stopy', _stopy, Math.max);
|
|
|
|
this.updateVal(this.nextData, 'startx', _startx, Math.min);
|
|
this.updateVal(this.nextData, 'starty', _starty, Math.min);
|
|
this.updateVal(this.nextData, 'stopx', _stopx, Math.max);
|
|
this.updateVal(this.nextData, 'stopy', _stopy, Math.max);
|
|
}
|
|
|
|
init() {
|
|
this.data = {
|
|
startx: undefined,
|
|
stopx: undefined,
|
|
starty: undefined,
|
|
stopy: undefined,
|
|
widthLimit: undefined,
|
|
};
|
|
setConf(parser.yy.getConfig());
|
|
}
|
|
|
|
bumpLastMargin(margin) {
|
|
this.data.stopx += margin;
|
|
this.data.stopy += margin;
|
|
}
|
|
}
|
|
|
|
const personFont = (cnf) => {
|
|
return {
|
|
fontFamily: cnf.personFontFamily,
|
|
fontSize: cnf.personFontSize,
|
|
fontWeight: cnf.personFontWeight,
|
|
};
|
|
};
|
|
|
|
const systemFont = (cnf) => {
|
|
return {
|
|
fontFamily: cnf.systemFontFamily,
|
|
fontSize: cnf.systemFontSize,
|
|
fontWeight: cnf.systemFontWeight,
|
|
};
|
|
};
|
|
|
|
const boundaryFont = (cnf) => {
|
|
return {
|
|
fontFamily: cnf.boundaryFontFamily,
|
|
fontSize: cnf.boundaryFontSize,
|
|
fontWeight: cnf.boundaryFontWeight,
|
|
};
|
|
};
|
|
|
|
const messageFont = (cnf) => {
|
|
return {
|
|
fontFamily: cnf.messageFontFamily,
|
|
fontSize: cnf.messageFontSize,
|
|
fontWeight: cnf.messageFontWeight,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @param textType
|
|
* @param c4Shape
|
|
* @param c4ShapeTextWrap
|
|
* @param textConf
|
|
* @param textLimitWidth
|
|
*/
|
|
function setC4ShapeText(textType, c4Shape, c4ShapeTextWrap, textConf, textLimitWidth) {
|
|
if (!c4Shape[textType].width) {
|
|
if (c4ShapeTextWrap) {
|
|
c4Shape[textType].text = wrapLabel(c4Shape[textType].text, textLimitWidth, textConf);
|
|
c4Shape[textType].labelLines = c4Shape[textType].text.split(common.lineBreakRegex).length;
|
|
c4Shape[textType].width = textLimitWidth;
|
|
c4Shape[textType].height = c4Shape[textType].labelLines * (textConf.fontSize + 2);
|
|
} else {
|
|
let lines = c4Shape[textType].text.split(common.lineBreakRegex);
|
|
c4Shape[textType].labelLines = lines.length;
|
|
let lineHeight = 0;
|
|
c4Shape[textType].height = 0;
|
|
c4Shape[textType].width = 0;
|
|
for (let i = 0; i < lines.length; i++) {
|
|
c4Shape[textType].width = Math.max(
|
|
calculateTextWidth(lines[i], textConf),
|
|
c4Shape[textType].width
|
|
);
|
|
lineHeight = calculateTextHeight(lines[i], textConf);
|
|
c4Shape[textType].height = c4Shape[textType].height + lineHeight;
|
|
}
|
|
// c4Shapes[textType].height = c4Shapes[textType].labelLines * textConf.fontSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
export const drawBoundary = function (diagram, boundary, bounds) {
|
|
boundary.x = bounds.data.startx;
|
|
boundary.y = bounds.data.starty;
|
|
boundary.width = bounds.data.stopx - bounds.data.startx;
|
|
boundary.height = bounds.data.stopy - bounds.data.starty;
|
|
|
|
boundary.label.y = conf.c4ShapeMargin - 35;
|
|
|
|
let boundaryTextWrap = boundary.wrap && conf.wrap;
|
|
let boundaryLabelConf = boundaryFont(conf);
|
|
boundaryLabelConf.fontSize = boundaryLabelConf.fontSize + 2;
|
|
boundaryLabelConf.fontWeight = 'bold';
|
|
let textLimitWidth = calculateTextWidth(boundary.label.text, boundaryLabelConf);
|
|
setC4ShapeText('label', boundary, boundaryTextWrap, boundaryLabelConf, textLimitWidth);
|
|
|
|
svgDraw.drawBoundary(diagram, boundary, conf);
|
|
};
|
|
|
|
export const drawPersonOrSystemArray = function (
|
|
currentBounds,
|
|
diagram,
|
|
personOrSystemArray,
|
|
personOrSystemKeys
|
|
) {
|
|
// Draw the personOrSystemArray
|
|
|
|
// let prevWidth = currentBounds.data.stopx;
|
|
// let prevMarginX = conf.c4ShapeMargin;
|
|
// let prevMarginY = conf.c4ShapeMargin;
|
|
// let maxHeight = currentBounds.data.starty;
|
|
|
|
for (let i = 0; i < personOrSystemKeys.length; i++) {
|
|
const personOrSystem = personOrSystemArray[personOrSystemKeys[i]];
|
|
|
|
let imageWidth = 0,
|
|
imageHeight = 0;
|
|
switch (personOrSystem.type) {
|
|
case 'person':
|
|
case 'external_person':
|
|
imageWidth = 48;
|
|
imageHeight = 48;
|
|
break;
|
|
}
|
|
|
|
if (!personOrSystem.typeLabelWidth) {
|
|
let personOrSystemTypeConf = personFont(conf);
|
|
personOrSystemTypeConf.fontSize = personOrSystemTypeConf.fontSize - 2;
|
|
personOrSystem.typeLabelWidth = calculateTextWidth(
|
|
'<<' + personOrSystem.type + '>>',
|
|
personOrSystemTypeConf
|
|
);
|
|
personOrSystem.typeLabelHeight = personOrSystemTypeConf.fontSize + 2;
|
|
|
|
switch (personOrSystem.type) {
|
|
case 'system_db':
|
|
case 'external_system_db':
|
|
personOrSystem.typeLabelY = conf.c4ShapePadding;
|
|
break;
|
|
default:
|
|
personOrSystem.typeLabelY = conf.c4ShapePadding - 5;
|
|
break;
|
|
}
|
|
}
|
|
|
|
let personOrSystemTextWrap = personOrSystem.wrap && conf.wrap;
|
|
let textLimitWidth = conf.width - conf.c4ShapePadding * 2;
|
|
|
|
let personOrSystemLabelConf = personFont(conf);
|
|
personOrSystemLabelConf.fontSize = personOrSystemLabelConf.fontSize + 2;
|
|
personOrSystemLabelConf.fontWeight = 'bold';
|
|
|
|
setC4ShapeText(
|
|
'label',
|
|
personOrSystem,
|
|
personOrSystemTextWrap,
|
|
personOrSystemLabelConf,
|
|
textLimitWidth
|
|
);
|
|
personOrSystem['label'].Y =
|
|
conf.c4ShapePadding + personOrSystem.typeLabelHeight + imageHeight + 10;
|
|
|
|
let personOrSystemDescrConf = personFont(conf);
|
|
setC4ShapeText(
|
|
'descr',
|
|
personOrSystem,
|
|
personOrSystemTextWrap,
|
|
personOrSystemDescrConf,
|
|
textLimitWidth
|
|
);
|
|
personOrSystem['descr'].Y =
|
|
conf.c4ShapePadding +
|
|
personOrSystem.typeLabelHeight +
|
|
imageHeight +
|
|
5 +
|
|
personOrSystem.label.height +
|
|
conf.personFontSize +
|
|
2;
|
|
|
|
// Add some rendering data to the object
|
|
let rectWidth =
|
|
Math.max(personOrSystem.label.width, personOrSystem.descr.width) + conf.c4ShapePadding * 2;
|
|
let rectHeight =
|
|
conf.c4ShapePadding +
|
|
personOrSystem.typeLabelHeight +
|
|
imageHeight +
|
|
personOrSystem.label.height +
|
|
conf.personFontSize +
|
|
2 +
|
|
personOrSystem.descr.height;
|
|
|
|
personOrSystem.width = Math.max(personOrSystem.width || conf.width, rectWidth, conf.width);
|
|
personOrSystem.height = Math.max(personOrSystem.height || conf.height, rectHeight, conf.height);
|
|
personOrSystem.margin = personOrSystem.margin || conf.c4ShapeMargin;
|
|
|
|
currentBounds.insert(personOrSystem);
|
|
|
|
const height = svgDraw.drawPersonOrSystem(diagram, personOrSystem, conf);
|
|
}
|
|
|
|
currentBounds.bumpLastMargin(conf.c4ShapeMargin);
|
|
};
|
|
|
|
class Point {
|
|
constructor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
|
|
/* * *
|
|
* Get the intersection of the line between the center point of a rectangle and a point outside the rectangle.
|
|
* Algorithm idea.
|
|
* Using a point outside the rectangle as the coordinate origin, the graph is divided into four quadrants, and each quadrant is divided into two cases, with separate treatment on the coordinate axes
|
|
* 1. The case of coordinate axes.
|
|
* 1. The case of the negative x-axis
|
|
* 2. The case of the positive x-axis
|
|
* 3. The case of the positive y-axis
|
|
* 4. The negative y-axis case
|
|
* 2. Quadrant cases.
|
|
* 2.1. first quadrant: the case where the line intersects the left side of the rectangle; the case where it intersects the lower side of the rectangle
|
|
* 2.2. second quadrant: the case where the line intersects the right side of the rectangle; the case where it intersects the lower edge of the rectangle
|
|
* 2.3. third quadrant: the case where the line intersects the right side of the rectangle; the case where it intersects the upper edge of the rectangle
|
|
* 2.4. fourth quadrant: the case where the line intersects the left side of the rectangle; the case where it intersects the upper side of the rectangle
|
|
*
|
|
*/
|
|
let getIntersectPoint = function (fromNode, endPoint) {
|
|
let x1 = fromNode.x;
|
|
|
|
let y1 = fromNode.y;
|
|
|
|
let x2 = endPoint.x;
|
|
|
|
let y2 = endPoint.y;
|
|
|
|
let fromCenterX = x1 + fromNode.width / 2;
|
|
|
|
let fromCenterY = y1 + fromNode.height / 2;
|
|
|
|
let dx = Math.abs(x1 - x2);
|
|
|
|
let dy = Math.abs(y1 - y2);
|
|
|
|
let tanDYX = dy / dx;
|
|
|
|
let fromDYX = fromNode.height / fromNode.width;
|
|
|
|
let returnPoint = null;
|
|
|
|
if (y1 == y2 && x1 < x2) {
|
|
returnPoint = new Point(x1 + fromNode.width, fromCenterY);
|
|
} else if (y1 == y2 && x1 > x2) {
|
|
returnPoint = new Point(x1, fromCenterY);
|
|
} else if (x1 == x2 && y1 < y2) {
|
|
returnPoint = new Point(fromCenterX, y1 + fromNode.height);
|
|
} else if (x1 == x2 && y1 > y2) {
|
|
returnPoint = new Point(fromCenterX, y1);
|
|
}
|
|
|
|
if (x1 > x2 && y1 < y2) {
|
|
if (fromDYX >= tanDYX) {
|
|
returnPoint = new Point(x1, fromCenterY + (tanDYX * fromNode.width) / 2);
|
|
} else {
|
|
returnPoint = new Point(
|
|
fromCenterX - ((dx / dy) * fromNode.height) / 2,
|
|
y1 + fromNode.height
|
|
);
|
|
}
|
|
} else if (x1 < x2 && y1 < y2) {
|
|
//
|
|
if (fromDYX >= tanDYX) {
|
|
returnPoint = new Point(x1 + fromNode.width, fromCenterY + (tanDYX * fromNode.width) / 2);
|
|
} else {
|
|
returnPoint = new Point(
|
|
fromCenterX + ((dx / dy) * fromNode.height) / 2,
|
|
y1 + fromNode.height
|
|
);
|
|
}
|
|
} else if (x1 < x2 && y1 > y2) {
|
|
if (fromDYX >= tanDYX) {
|
|
returnPoint = new Point(x1 + fromNode.width, fromCenterY - (tanDYX * fromNode.width) / 2);
|
|
} else {
|
|
returnPoint = new Point(fromCenterX + ((fromNode.height / 2) * dx) / dy, y1);
|
|
}
|
|
} else if (x1 > x2 && y1 > y2) {
|
|
if (fromDYX >= tanDYX) {
|
|
returnPoint = new Point(x1, fromCenterY - (fromNode.width / 2) * tanDYX);
|
|
} else {
|
|
returnPoint = new Point(fromCenterX - ((fromNode.height / 2) * dx) / dy, y1);
|
|
}
|
|
}
|
|
return returnPoint;
|
|
};
|
|
|
|
let getIntersectPoints = function (fromNode, endNode) {
|
|
let endIntersectPoint = { x: 0, y: 0 };
|
|
endIntersectPoint.x = endNode.x + endNode.width / 2;
|
|
endIntersectPoint.y = endNode.y + endNode.height / 2;
|
|
let startPoint = getIntersectPoint(fromNode, endIntersectPoint);
|
|
|
|
endIntersectPoint.x = fromNode.x + fromNode.width / 2;
|
|
endIntersectPoint.y = fromNode.y + fromNode.height / 2;
|
|
let endPoint = getIntersectPoint(endNode, endIntersectPoint);
|
|
return { startPoint: startPoint, endPoint: endPoint };
|
|
};
|
|
|
|
export const drawRels = function (diagram, rels, getC4ShapeObj) {
|
|
for (let rel of rels) {
|
|
let relTextWrap = rel.wrap && conf.wrap;
|
|
let relConf = messageFont(conf);
|
|
let textLimitWidth = calculateTextWidth(rel.label.text, relConf);
|
|
setC4ShapeText('label', rel, relTextWrap, relConf, textLimitWidth);
|
|
|
|
if (rel.techn && rel.techn.text !== '') {
|
|
textLimitWidth = calculateTextWidth(rel.techn.text, relConf);
|
|
setC4ShapeText('techn', rel, relTextWrap, relConf, textLimitWidth);
|
|
}
|
|
|
|
if (rel.descr && rel.descr.text !== '') {
|
|
textLimitWidth = calculateTextWidth(rel.descr.text, relConf);
|
|
setC4ShapeText('descr', rel, relTextWrap, relConf, textLimitWidth);
|
|
}
|
|
|
|
let fromNode = getC4ShapeObj(rel.from);
|
|
let endNode = getC4ShapeObj(rel.to);
|
|
let points = getIntersectPoints(fromNode, endNode);
|
|
rel.startPoint = points.startPoint;
|
|
rel.endPoint = points.endPoint;
|
|
}
|
|
svgDraw.drawRels(diagram, rels, conf);
|
|
};
|
|
|
|
export const setConf = function (cnf) {
|
|
assignWithDepth(conf, cnf);
|
|
|
|
if (cnf.fontFamily) {
|
|
conf.personFontFamily = conf.systemFontFamily = conf.messageFontFamily = cnf.fontFamily;
|
|
}
|
|
if (cnf.fontSize) {
|
|
conf.personFontSize = conf.systemFontSize = conf.messageFontSize = cnf.fontSize;
|
|
}
|
|
if (cnf.fontWeight) {
|
|
conf.personFontWeight = conf.systemFontWeight = conf.messageFontWeight = cnf.fontWeight;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Draws a sequenceDiagram in the tag with id: id based on the graph definition in text.
|
|
*
|
|
* @param {any} text
|
|
* @param {any} id
|
|
*/
|
|
export const draw = function (text, id) {
|
|
conf = configApi.getConfig().c4;
|
|
const securityLevel = configApi.getConfig().securityLevel;
|
|
// Handle root and ocument for when rendering in sanbox mode
|
|
let sandboxElement;
|
|
if (securityLevel === 'sandbox') {
|
|
sandboxElement = select('#i' + id);
|
|
}
|
|
const root =
|
|
securityLevel === 'sandbox'
|
|
? select(sandboxElement.nodes()[0].contentDocument.body)
|
|
: select('body');
|
|
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
|
|
|
parser.yy.clear();
|
|
parser.yy.setWrap(conf.wrap);
|
|
parser.parse(text + '\n');
|
|
|
|
log.debug(`C:${JSON.stringify(conf, null, 2)}`);
|
|
|
|
const diagram =
|
|
securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : select(`[id="${id}"]`);
|
|
|
|
svgDraw.insertComputerIcon(diagram);
|
|
svgDraw.insertDatabaseIcon(diagram);
|
|
svgDraw.insertClockIcon(diagram);
|
|
|
|
let screenBounds = new Bounds();
|
|
screenBounds.setData(
|
|
conf.diagramMarginX,
|
|
conf.diagramMarginX,
|
|
conf.diagramMarginY,
|
|
conf.diagramMarginY
|
|
);
|
|
|
|
screenBounds.data.widthLimit = screen.availWidth;
|
|
globalBoundaryMaxX = conf.diagramMarginX;
|
|
globalBoundaryMaxY = conf.diagramMarginY;
|
|
|
|
const title = parser.yy.getTitle();
|
|
const c4type = parser.yy.getC4Type();
|
|
switch (c4type) {
|
|
case 'C4Context':
|
|
/**
|
|
* @param parentBoundaryAlias
|
|
* @param parentBounds
|
|
* @param currentBoundarys
|
|
*/
|
|
function drawInsideBoundary(parentBoundaryAlias, parentBounds, currentBoundarys) {
|
|
let currentBounds = new Bounds();
|
|
// Calculate the width limit of the boundar. label/type 的长度,
|
|
currentBounds.data.widthLimit = Math.min(
|
|
conf.width * conf.c4ShapeInRow + conf.c4ShapeMargin * (conf.c4ShapeInRow + 1),
|
|
parentBounds.data.widthLimit / Math.min(conf.c4BoundaryInRow, currentBoundarys.length)
|
|
);
|
|
for (let i = 0; i < currentBoundarys.length; i++) {
|
|
let currentBoundary = currentBoundarys[i];
|
|
if (i == 0) {
|
|
// Calculate the drawing start point of the currentBoundarys.
|
|
let _x = parentBounds.data.startx + conf.diagramMarginX;
|
|
let _y = parentBounds.data.stopy + conf.diagramMarginY;
|
|
|
|
currentBounds.setData(_x, _x, _y, _y);
|
|
} else {
|
|
// Calculate the drawing start point of the currentBoundarys.
|
|
let _x =
|
|
currentBounds.data.stopx !== currentBounds.data.startx
|
|
? currentBounds.data.stopx + conf.diagramMarginX
|
|
: currentBounds.data.startx;
|
|
let _y = currentBounds.data.starty;
|
|
|
|
currentBounds.setData(_x, _x, _y, _y);
|
|
}
|
|
currentBounds.name = currentBoundary.alias;
|
|
let currentPersonOrSystemArray = parser.yy.getPersonOrSystemArray(currentBoundary.alias);
|
|
let currentPersonOrSystemKeys = parser.yy.getPersonOrSystemKeys(currentBoundary.alias);
|
|
|
|
if (currentPersonOrSystemKeys.length > 0) {
|
|
drawPersonOrSystemArray(
|
|
currentBounds,
|
|
diagram,
|
|
currentPersonOrSystemArray,
|
|
currentPersonOrSystemKeys
|
|
);
|
|
}
|
|
parentBoundaryAlias = currentBoundary.alias;
|
|
let nextCurrentBoundarys = parser.yy.getBoundarys(parentBoundaryAlias);
|
|
|
|
if (nextCurrentBoundarys.length > 0) {
|
|
// draw boundary inside currentBoundary
|
|
// bounds.init();
|
|
// parentBoundaryWidthLimit = bounds.data.stopx - bounds.startx;
|
|
drawInsideBoundary(parentBoundaryAlias, currentBounds, nextCurrentBoundarys);
|
|
}
|
|
// draw boundary
|
|
if (currentBoundary.alias !== 'global')
|
|
drawBoundary(diagram, currentBoundary, currentBounds);
|
|
parentBounds.data.stopy = Math.max(
|
|
currentBounds.data.stopy + conf.c4ShapeMargin,
|
|
parentBounds.data.stopy
|
|
);
|
|
parentBounds.data.stopx = Math.max(
|
|
currentBounds.data.stopx + conf.c4ShapeMargin,
|
|
parentBounds.data.stopx
|
|
);
|
|
globalBoundaryMaxX = Math.max(globalBoundaryMaxX, parentBounds.data.stopx);
|
|
globalBoundaryMaxY = Math.max(globalBoundaryMaxY, parentBounds.data.stopy);
|
|
}
|
|
}
|
|
|
|
let currentBoundarys = parser.yy.getBoundarys('');
|
|
drawInsideBoundary('', screenBounds, currentBoundarys);
|
|
|
|
break;
|
|
}
|
|
|
|
// The arrow head definition is attached to the svg once
|
|
svgDraw.insertArrowHead(diagram);
|
|
svgDraw.insertArrowEnd(diagram);
|
|
svgDraw.insertArrowCrossHead(diagram);
|
|
svgDraw.insertArrowFilledHead(diagram);
|
|
|
|
drawRels(diagram, parser.yy.getRels(), parser.yy.getPersonOrSystem);
|
|
|
|
screenBounds.data.stopx = globalBoundaryMaxX;
|
|
screenBounds.data.stopy = globalBoundaryMaxY;
|
|
|
|
const box = screenBounds.data;
|
|
|
|
// Make sure the height of the diagram supports long menus.
|
|
let boxHeight = box.stopy - box.starty;
|
|
|
|
let height = boxHeight + 2 * conf.diagramMarginY;
|
|
|
|
// Make sure the width of the diagram supports wide menus.
|
|
let boxWidth = box.stopx - box.startx;
|
|
const width = boxWidth + 2 * conf.diagramMarginX;
|
|
|
|
if (title) {
|
|
diagram
|
|
.append('text')
|
|
.text(title)
|
|
.attr('x', (box.stopx - box.startx) / 2 - 4 * conf.diagramMarginX)
|
|
.attr('y', -25);
|
|
}
|
|
|
|
configureSvgSize(diagram, height, width, conf.useMaxWidth);
|
|
|
|
const extraVertForTitle = title ? 60 : 0;
|
|
diagram.attr(
|
|
'viewBox',
|
|
box.startx -
|
|
conf.diagramMarginX +
|
|
' -' +
|
|
(conf.diagramMarginY + extraVertForTitle) +
|
|
' ' +
|
|
width +
|
|
' ' +
|
|
(height + extraVertForTitle)
|
|
);
|
|
|
|
addSVGAccessibilityFields(parser.yy, diagram, id);
|
|
log.debug(`models:`, box);
|
|
};
|
|
|
|
export default {
|
|
drawPersonOrSystemArray,
|
|
drawBoundary,
|
|
setConf,
|
|
draw,
|
|
};
|